Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions py_spring_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from py_spring_core.core.entities.entity_provider.entity_provider import EntityProvider
from py_spring_core.core.entities.middlewares.middleware import Middleware
from py_spring_core.core.entities.middlewares.middleware_registry import (
MiddlewareRegistry,
MiddlewareRegistry, MiddlewareConfiguration
)
from py_spring_core.core.entities.properties.properties import Properties
from py_spring_core.core.interfaces.application_context_required import (
Expand Down Expand Up @@ -44,6 +44,7 @@
"EventListener",
"Middleware",
"MiddlewareRegistry",
"MiddlewareConfiguration",
"GracefulShutdownHandler",
"ShutdownType",
]
"ShutdownType"
]
20 changes: 13 additions & 7 deletions py_spring_core/core/application/py_spring_application.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import os
from typing import Any, Callable, Iterable, Optional, Type
from typing import Any, Callable, Iterable, Optional, Type, TypeVar

import uvicorn
from fastapi import APIRouter, FastAPI
Expand Down Expand Up @@ -30,6 +30,7 @@
from py_spring_core.core.entities.entity_provider.entity_provider import EntityProvider
from py_spring_core.core.entities.middlewares.middleware import Middleware
from py_spring_core.core.entities.middlewares.middleware_registry import (
MiddlewareConfiguration,
MiddlewareRegistry,
)
from py_spring_core.core.entities.properties.properties import Properties
Expand All @@ -45,6 +46,8 @@

import py_spring_core.core.utils as framework_utils


SingleInheritanceRequiredT = TypeVar("SingleInheritanceRequiredT", bound=SingleInheritanceRequired)
class PySpringApplication:
"""
The PySpringApplication class is the main entry point for the PySpring application.
Expand Down Expand Up @@ -237,7 +240,7 @@ def __init_controllers(self) -> None:
self.fastapi.include_router(router)
logger.debug(f"[CONTROLLER INIT] Controller {name} initialized")

def _init_external_handler(self, base_class: Type[SingleInheritanceRequired]) -> Type[Any] | None:
def _init_external_handler(self, base_class: Type[SingleInheritanceRequiredT]) -> Type[SingleInheritanceRequiredT] | None:
"""Initialize an external handler (middleware registry or graceful shutdown handler).

Args:
Expand Down Expand Up @@ -276,12 +279,15 @@ def _init_external_handler(self, base_class: Type[SingleInheritanceRequired]) ->
def __init_middlewares(self) -> None:
handler_type = MiddlewareRegistry.__name__
logger.debug(f"[{handler_type} INIT] Initialize middlewares...")
registry_cls = self._init_external_handler(MiddlewareRegistry)
if registry_cls is None:
middeware_configuration_cls = self._init_external_handler(MiddlewareConfiguration)
if middeware_configuration_cls is None:
return

registry: MiddlewareRegistry = registry_cls()
registry = MiddlewareRegistry()
logger.info(f"[{handler_type} INIT] Setup middlewares for registry: {registry.__class__.__name__}")
middeware_configuration_cls().configure_middlewares(registry)
logger.info(f"[{handler_type} INIT] Middlewares setup for registry: {registry.__class__.__name__} completed")
middleware_classes: list[Type[Middleware]] = registry.get_middleware_classes()
logger.info(f"[{handler_type} INIT] Middleware classes: {', '.join([middleware_class.__name__ for middleware_class in middleware_classes])}")
for middleware_class in middleware_classes:
logger.debug(
f"[{handler_type} INIT] Inject dependencies for middleware: {middleware_class.__name__}"
Expand All @@ -305,7 +311,7 @@ def __init_graceful_shutdown(self) -> None:
self.shutdown_handler = handler_cls(
timeout_seconds=shutdown_config.timeout_seconds,
timeout_enabled=shutdown_config.enabled
)
) # type: ignore
logger.debug(f"[{handler_type} INIT] Graceful shutdown initialized")

def __configure_uvicorn_logging(self):
Expand Down
164 changes: 155 additions & 9 deletions py_spring_core/core/entities/middlewares/middleware_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
)


class MiddlewareRegistry(SingleInheritanceRequired["MiddlewareRegistry"], ABC):
class MiddlewareRegistry:
"""
Middleware registry for managing all middlewares

Expand All @@ -31,20 +31,153 @@ class MiddlewareRegistry(SingleInheritanceRequired["MiddlewareRegistry"], ABC):
Response: route β†’ MiddlewareA β†’ MiddlewareB
This stacking behavior ensures that middlewares are executed in a predictable and controllable order.
"""

@abstractmethod

def __init__(self):
"""
Initialize the middleware registry.
"""
self._middlewares: list[Type[Middleware]] = []

def add_middleware(self, middleware_class: Type[Middleware]) -> None:
"""
Add middleware to the end of the list.

Args:
middleware_class: The middleware class to add

Raises:
ValueError: If middleware is already registered
"""
if middleware_class in self._middlewares:
raise ValueError(f"Middleware {middleware_class.__name__} is already registered")
self._middlewares.append(middleware_class)

def add_at_index(self, index: int, middleware_class: Type[Middleware]) -> None:
"""
Insert middleware at a specific index position.

Args:
index: The position to insert at (0-based)
middleware_class: The middleware class to add

Raises:
ValueError: If middleware is already registered or index is invalid
"""
if middleware_class in self._middlewares:
raise ValueError(f"Middleware {middleware_class.__name__} is already registered")
if index < 0 or index > len(self._middlewares):
raise ValueError(f"Index {index} is out of range (0-{len(self._middlewares)})")
self._middlewares.insert(index, middleware_class)

def add_before(self, target_middleware: Type[Middleware], middleware_class: Type[Middleware]) -> None:
"""
Insert middleware before the target middleware.

Args:
target_middleware: The middleware to insert before
middleware_class: The middleware class to add

Raises:
ValueError: If middleware is already registered or target not found
"""
if middleware_class in self._middlewares:
raise ValueError(f"Middleware {middleware_class.__name__} is already registered")
if target_middleware not in self._middlewares:
raise ValueError(f"Target middleware {target_middleware.__name__} not found")
index = self._middlewares.index(target_middleware)
self._middlewares.insert(index, middleware_class)

def add_after(self, target_middleware: Type[Middleware], middleware_class: Type[Middleware]) -> None:
"""
Insert middleware after the target middleware.

Args:
target_middleware: The middleware to insert after
middleware_class: The middleware class to add

Raises:
ValueError: If middleware is already registered or target not found
"""
if middleware_class in self._middlewares:
raise ValueError(f"Middleware {middleware_class.__name__} is already registered")
if target_middleware not in self._middlewares:
raise ValueError(f"Target middleware {target_middleware.__name__} not found")
index = self._middlewares.index(target_middleware)
self._middlewares.insert(index + 1, middleware_class)

def remove_middleware(self, middleware_class: Type[Middleware]) -> None:
"""
Remove a middleware from the registry.

Args:
middleware_class: The middleware class to remove

Raises:
ValueError: If middleware is not found
"""
if middleware_class not in self._middlewares:
raise ValueError(f"Middleware {middleware_class.__name__} not found")
self._middlewares.remove(middleware_class)

def clear_middlewares(self) -> None:
"""Remove all middlewares from the registry."""
self._middlewares.clear()

def has_middleware(self, middleware_class: Type[Middleware]) -> bool:
"""
Check if a middleware is registered.

Args:
middleware_class: The middleware class to check

Returns:
bool: True if middleware is registered, False otherwise
"""
return middleware_class in self._middlewares

def get_middleware_count(self) -> int:
"""
Get the number of registered middlewares.

Returns:
int: Number of registered middlewares
"""
return len(self._middlewares)

def get_middleware_index(self, middleware_class: Type[Middleware]) -> int:
"""
Get the index of a middleware in the registry.

Args:
middleware_class: The middleware class to find

Returns:
int: The index of the middleware

Raises:
ValueError: If middleware is not found
"""
if middleware_class not in self._middlewares:
raise ValueError(f"Middleware {middleware_class.__name__} not found")
return self._middlewares.index(middleware_class)



def get_middleware_classes(self) -> list[Type[Middleware]]:
"""
Get all registered middleware classes

Get all registered middleware classes.
Returns:
List[Type[Middleware]]: List of middleware classes
List[Type[Middleware]]: List of middleware classes in registration order
"""
pass

return self._middlewares.copy()
def apply_middlewares(self, app: FastAPI) -> FastAPI:
"""
Apply middlewares to FastAPI application
Apply middlewares to FastAPI application.

Iterates through all registered middlewares and applies them to the FastAPI
application instance in the order they were registered.

Args:
app: FastAPI application instance
Expand All @@ -55,3 +188,16 @@ def apply_middlewares(self, app: FastAPI) -> FastAPI:
for middleware_class in self.get_middleware_classes():
app.add_middleware(middleware_class)
return app



class MiddlewareConfiguration(SingleInheritanceRequired["MiddlewareConfiguration"]):
"""
Middleware configuration for managing middleware registration and execution order.
"""

def configure_middlewares(self, registry: MiddlewareRegistry) -> None:
"""
Setup middlewares for the registry.
"""
pass
18 changes: 18 additions & 0 deletions py_spring_core/exception_handler/decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@


from functools import wraps
from typing import Any, Callable, Type


from py_spring_core.exception_handler.exception_handler_registry import ExceptionHandlerRegistry


def ExceptionHandler(exception_cls: Type[Exception]) -> Callable[[Callable[[Exception], Any]], Callable]:
def decorator(func: Callable[[Exception], Any]) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
return func(*args, **kwargs)
ExceptionHandlerRegistry.register(exception_cls, wrapper)
return wrapper

return decorator
25 changes: 25 additions & 0 deletions py_spring_core/exception_handler/exception_handler_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@


from typing import Any, Callable, Type, TypeVar

from loguru import logger

E = TypeVar('E', bound=Exception)

class ExceptionHandlerRegistry:
_handlers: dict[str, Callable[[Any], Any]] = {}

@classmethod
def register(cls, exception_cls: Type[E], handler: Callable[[E], Any]):
key = exception_cls.__name__
logger.debug(f"Registering exception handler for {key}: {handler.__name__}")
if key in cls._handlers:
error_message = f"Exception handler for {exception_cls} already registered"
logger.error(error_message)
raise RuntimeError(error_message)

cls._handlers[exception_cls.__name__] = handler

@classmethod
def get_handler(cls, exception_cls: Type[E]) -> Callable[[E], Any]:
return cls._handlers[exception_cls.__name__]
Loading