From 72418f6bdb274b78c709ffaae59570d6956a655e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20=D0=93=D0=BB=D0=B0=D0=B7=D1=83?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2?= Date: Tue, 2 Sep 2025 14:33:34 +0300 Subject: [PATCH] refactor: Add typing stubs for PyYAML and improve type hints in logging utilities and extensions --- .gitignore | 1 + poetry.lock | 14 +++++++++++++- pyproject.toml | 3 ++- pyserve/cli.py | 2 +- pyserve/extensions.py | 10 +++++----- pyserve/logging_utils.py | 23 +++++++++-------------- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index d31fce4..ead5fd4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ logs/* static/* +.DS_Store \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index fe157ac..44eeced 100644 --- a/poetry.lock +++ b/poetry.lock @@ -653,6 +653,18 @@ typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.13\"" [package.extras] 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]] name = "typing-extensions" version = "4.15.0" @@ -948,4 +960,4 @@ dev = ["black", "flake8", "isort", "mypy", "pytest", "pytest-cov"] [metadata] lock-version = "2.1" python-versions = ">=3.12" -content-hash = "a69856492efdf3ed2272517f43842f06b7199519b2da459c6aadc863e2429c45" +content-hash = "e145aef2574fcda0c0d45b8620988baf25f386a1b6ccf199c56210cbc0e3aa76" diff --git a/pyproject.toml b/pyproject.toml index e5ebfb5..2e2d814 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,8 @@ requires-python = ">=3.12" dependencies = [ "starlette (>=0.47.3,<0.48.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] diff --git a/pyserve/cli.py b/pyserve/cli.py index f807dd3..5d5e033 100644 --- a/pyserve/cli.py +++ b/pyserve/cli.py @@ -5,7 +5,7 @@ from pathlib import Path from . import PyServeServer, Config, __version__ -def main(): +def main() -> None: parser = argparse.ArgumentParser( description="PyServe - HTTP web server", prog="pyserve" diff --git a/pyserve/extensions.py b/pyserve/extensions.py index 53e815f..2e37c40 100644 --- a/pyserve/extensions.py +++ b/pyserve/extensions.py @@ -1,5 +1,5 @@ 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.responses import Response from .logging_utils import get_logger @@ -101,7 +101,7 @@ class MonitoringExtension(Extension): super().__init__(config) self.request_count = 0 self.error_count = 0 - self.response_times = [] + self.response_times: list[float] = [] self.enable_metrics = config.get("enable_metrics", True) async def process_request(self, request: Request) -> Optional[Response]: @@ -138,16 +138,16 @@ class MonitoringExtension(Extension): class ExtensionManager: - def __init__(self): + def __init__(self) -> None: self.extensions: List[Extension] = [] - self.extension_registry = { + self.extension_registry: Dict[str, Type[Extension]] = { "routing": RoutingExtension, "security": SecurityExtension, "caching": CachingExtension, "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 def load_extension(self, extension_type: str, config: Dict[str, Any]) -> None: diff --git a/pyserve/logging_utils.py b/pyserve/logging_utils.py index a35d0dc..eb7869e 100644 --- a/pyserve/logging_utils.py +++ b/pyserve/logging_utils.py @@ -1,8 +1,3 @@ -""" -Кастомная система логирования для PyServe -Управляет логгерами всех пакетов и модулей, включая uvicorn и starlette -""" - import logging import logging.handlers import sys @@ -14,7 +9,7 @@ from . import __version__ 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, 'getMessage'): msg = record.getMessage() @@ -41,12 +36,12 @@ class PyServeFormatter(logging.Formatter): '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) self.use_colors = use_colors and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty() self.show_module = show_module - def format(self, record): + def format(self, record: logging.LogRecord) -> str: if self.use_colors: levelname = record.levelname if levelname in self.COLORS: @@ -69,12 +64,12 @@ class AccessLogHandler(logging.Handler): super().__init__() self.access_logger = logging.getLogger(logger_name) - def emit(self, record): + def emit(self, record: logging.LogRecord) -> None: self.access_logger.handle(record) class PyServeLogManager: - def __init__(self): + def __init__(self) -> None: self.configured = False self.handlers: Dict[str, logging.Handler] = {} self.loggers: Dict[str, logging.Logger] = {} @@ -138,10 +133,10 @@ class PyServeLogManager: pyserve_logger.setLevel(getattr(logging, level)) self.loggers['pyserve'] = pyserve_logger - pyserve_logger.info(f"PyServe v{__version__} - Система логирования инициализирована") - pyserve_logger.info(f"Уровень логирования: {level}") - pyserve_logger.info(f"Консольный вывод: {'включен' if console_output else 'отключен'}") - pyserve_logger.info(f"Файл логов: {log_file if log_file else 'отключен'}") + pyserve_logger.info(f"PyServe v{__version__} - Logger initialized") + pyserve_logger.info(f"Logging level: {level}") + pyserve_logger.info(f"Console output: {'enabled' if console_output else 'disabled'}") + pyserve_logger.info(f"Log file: {log_file if log_file else 'disabled'}") self.configured = True