refactor: Add typing stubs for PyYAML and improve type hints in logging utilities and extensions

This commit is contained in:
Илья Глазунов 2025-09-02 14:33:34 +03:00
parent 6b157d7626
commit 72418f6bdb
6 changed files with 31 additions and 22 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ logs/*
static/* static/*
.DS_Store

14
poetry.lock generated
View File

@ -653,6 +653,18 @@ typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\""
[package.extras] [package.extras]
full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"]
[[package]]
name = "types-pyyaml"
version = "6.0.12.20250822"
description = "Typing stubs for PyYAML"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "types_pyyaml-6.0.12.20250822-py3-none-any.whl", hash = "sha256:1fe1a5e146aa315483592d292b72a172b65b946a6d98aa6ddd8e4aa838ab7098"},
{file = "types_pyyaml-6.0.12.20250822.tar.gz", hash = "sha256:259f1d93079d335730a9db7cff2bcaf65d7e04b4a56b5927d49a612199b59413"},
]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.15.0" version = "4.15.0"
@ -948,4 +960,4 @@ dev = ["black", "flake8", "isort", "mypy", "pytest", "pytest-cov"]
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.12" python-versions = ">=3.12"
content-hash = "a69856492efdf3ed2272517f43842f06b7199519b2da459c6aadc863e2429c45" content-hash = "e145aef2574fcda0c0d45b8620988baf25f386a1b6ccf199c56210cbc0e3aa76"

View File

@ -11,7 +11,8 @@ requires-python = ">=3.12"
dependencies = [ dependencies = [
"starlette (>=0.47.3,<0.48.0)", "starlette (>=0.47.3,<0.48.0)",
"uvicorn[standard] (>=0.35.0,<0.36.0)", "uvicorn[standard] (>=0.35.0,<0.36.0)",
"pyyaml (>=6.0,<7.0)" "pyyaml (>=6.0,<7.0)",
"types-pyyaml (>=6.0.12.20250822,<7.0.0.0)",
] ]
[project.scripts] [project.scripts]

View File

@ -5,7 +5,7 @@ from pathlib import Path
from . import PyServeServer, Config, __version__ from . import PyServeServer, Config, __version__
def main(): def main() -> None:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="PyServe - HTTP web server", description="PyServe - HTTP web server",
prog="pyserve" prog="pyserve"

View File

@ -1,5 +1,5 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Dict, Any, List, Optional from typing import Dict, Any, List, Optional, Type
from starlette.requests import Request from starlette.requests import Request
from starlette.responses import Response from starlette.responses import Response
from .logging_utils import get_logger from .logging_utils import get_logger
@ -101,7 +101,7 @@ class MonitoringExtension(Extension):
super().__init__(config) super().__init__(config)
self.request_count = 0 self.request_count = 0
self.error_count = 0 self.error_count = 0
self.response_times = [] self.response_times: list[float] = []
self.enable_metrics = config.get("enable_metrics", True) self.enable_metrics = config.get("enable_metrics", True)
async def process_request(self, request: Request) -> Optional[Response]: async def process_request(self, request: Request) -> Optional[Response]:
@ -138,16 +138,16 @@ class MonitoringExtension(Extension):
class ExtensionManager: class ExtensionManager:
def __init__(self): def __init__(self) -> None:
self.extensions: List[Extension] = [] self.extensions: List[Extension] = []
self.extension_registry = { self.extension_registry: Dict[str, Type[Extension]] = {
"routing": RoutingExtension, "routing": RoutingExtension,
"security": SecurityExtension, "security": SecurityExtension,
"caching": CachingExtension, "caching": CachingExtension,
"monitoring": MonitoringExtension "monitoring": MonitoringExtension
} }
def register_extension_type(self, name: str, extension_class: type) -> None: def register_extension_type(self, name: str, extension_class: Type[Extension]) -> None:
self.extension_registry[name] = extension_class self.extension_registry[name] = extension_class
def load_extension(self, extension_type: str, config: Dict[str, Any]) -> None: def load_extension(self, extension_type: str, config: Dict[str, Any]) -> None:

View File

@ -1,8 +1,3 @@
"""
Кастомная система логирования для PyServe
Управляет логгерами всех пакетов и модулей, включая uvicorn и starlette
"""
import logging import logging
import logging.handlers import logging.handlers
import sys import sys
@ -14,7 +9,7 @@ from . import __version__
class UvicornLogFilter(logging.Filter): class UvicornLogFilter(logging.Filter):
def filter(self, record): def filter(self, record: logging.LogRecord) -> bool:
if hasattr(record, 'name') and 'uvicorn.access' in record.name: if hasattr(record, 'name') and 'uvicorn.access' in record.name:
if hasattr(record, 'getMessage'): if hasattr(record, 'getMessage'):
msg = record.getMessage() msg = record.getMessage()
@ -41,12 +36,12 @@ class PyServeFormatter(logging.Formatter):
'RESET': '\033[0m' # Reset 'RESET': '\033[0m' # Reset
} }
def __init__(self, use_colors: bool = True, show_module: bool = True, *args, **kwargs): def __init__(self, use_colors: bool = True, show_module: bool = True, *args: Any, **kwargs: Any):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.use_colors = use_colors and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty() self.use_colors = use_colors and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty()
self.show_module = show_module self.show_module = show_module
def format(self, record): def format(self, record: logging.LogRecord) -> str:
if self.use_colors: if self.use_colors:
levelname = record.levelname levelname = record.levelname
if levelname in self.COLORS: if levelname in self.COLORS:
@ -69,12 +64,12 @@ class AccessLogHandler(logging.Handler):
super().__init__() super().__init__()
self.access_logger = logging.getLogger(logger_name) self.access_logger = logging.getLogger(logger_name)
def emit(self, record): def emit(self, record: logging.LogRecord) -> None:
self.access_logger.handle(record) self.access_logger.handle(record)
class PyServeLogManager: class PyServeLogManager:
def __init__(self): def __init__(self) -> None:
self.configured = False self.configured = False
self.handlers: Dict[str, logging.Handler] = {} self.handlers: Dict[str, logging.Handler] = {}
self.loggers: Dict[str, logging.Logger] = {} self.loggers: Dict[str, logging.Logger] = {}
@ -138,10 +133,10 @@ class PyServeLogManager:
pyserve_logger.setLevel(getattr(logging, level)) pyserve_logger.setLevel(getattr(logging, level))
self.loggers['pyserve'] = pyserve_logger self.loggers['pyserve'] = pyserve_logger
pyserve_logger.info(f"PyServe v{__version__} - Система логирования инициализирована") pyserve_logger.info(f"PyServe v{__version__} - Logger initialized")
pyserve_logger.info(f"Уровень логирования: {level}") pyserve_logger.info(f"Logging level: {level}")
pyserve_logger.info(f"Консольный вывод: {'включен' if console_output else 'отключен'}") pyserve_logger.info(f"Console output: {'enabled' if console_output else 'disabled'}")
pyserve_logger.info(f"Файл логов: {log_file if log_file else 'отключен'}") pyserve_logger.info(f"Log file: {log_file if log_file else 'disabled'}")
self.configured = True self.configured = True