/
liquid-g
/
liquid-code
Обзор
Документация
Войти
/
liquid-g
/
liquid-code
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/liquidcode/kernel/kernel.py
313 строк
12 KB
User
ci: настроен Black и flake8 для автоматического форматирования кода
04 июл 2026, 08:14
04 июл 2026, 08:14
4778ed6
Код
Авторство
О чём код?
""" Ядро фреймворка. Обрабатывает Request и возвращает Response через цепочку middleware и хуков. """ import logging import time from typing import Callable, List, Optional, Type from .argument_resolver import ArgumentResolver from ..container import Container from ..contracts import Request, Response from ..http import HttpResponse from ..profiler import ProfilerContext from ..routing import Router logger = logging.getLogger(__name__) class HttpNotFound(Exception): """Исключение для 404 Not Found.""" status_code = 404 class Kernel: """ Ядро приложения. Принимает Request, прогоняет через middleware, находит контроллер, вызывает его и возвращает Response. """ def __init__( self, router: Router, container: Container, response_class: Optional[Type[Response]] = None, profiler_enabled: bool = False, ) -> None: """ Args: router: Экземпляр роутера. container: DI-контейнер для создания контроллеров. response_class: Класс для создания объектов Response. Если не передан, используется HttpResponse. profiler_enabled: Включить профайлер (по умолчанию False). """ self.router = router self.container = container self.response_class = response_class or HttpResponse self.argument_resolver = ArgumentResolver(container) self.profiler_enabled = profiler_enabled self._middlewares: List[ Callable[[Request, Callable[[Request], Response]], Response] ] = [] # Хуки self._before_request_hooks: List[Callable[[Request], Optional[Response]]] = [] self._after_request_hooks: List[Callable[[Request, Response], Response]] = [] self._exception_hooks: List[ Callable[[Request, Exception], Optional[Response]] ] = [] self._terminate_hooks: List[Callable[[Request, Response], None]] = [] def enable_profiler(self) -> None: """Включает профайлер.""" self.profiler_enabled = True # ========== Middleware API ========== def add_middleware( self, middleware: Callable[[Request, Callable[[Request], Response]], Response], ) -> None: """ Добавляет middleware в цепочку обработки. Middleware должна принимать (request: Request, call_next: Callable[[Request], Response]) -> Response. """ self._middlewares.append(middleware) # ========== Хуки API ========== def add_before_request(self, hook: Callable[[Request], Optional[Response]]) -> None: self._before_request_hooks.append(hook) def add_after_request(self, hook: Callable[[Request, Response], Response]) -> None: self._after_request_hooks.append(hook) def add_exception( self, hook: Callable[[Request, Exception], Optional[Response]] ) -> None: self._exception_hooks.append(hook) def add_terminate(self, hook: Callable[[Request, Response], None]) -> None: self._terminate_hooks.append(hook) # ========== Основной метод ========== def handle(self, request: Request) -> Response: """ Основной метод обработки запроса. """ # 1. Хуки перед всеми middleware for hook in self._before_request_hooks: response = hook(request) if response is not None: return response # 2. Создаём цепочку глобальных middleware + контроллер profiler_context = None if self.profiler_enabled: profiler_context = ProfilerContext() request.set_attr("_profiler_context", profiler_context) def call_controller(req: Request) -> Response: if self.profiler_enabled: start = time.time() response = self._process_request(req) duration = time.time() - start context = req.get_attr("_profiler_context") if context: context.set_controller(duration) return response else: return self._process_request(req) chain = call_controller for mw in reversed(self._middlewares): if self.profiler_enabled: def wrap_middleware( middleware: Callable, next_step: Callable ) -> Callable: def wrapped(req: Request) -> Response: start = time.time() response = middleware(req, next_step) duration = time.time() - start context = req.get_attr("_profiler_context") if context: name = getattr(middleware, "__name__", str(middleware)) context.add_middleware(name, duration) return response return wrapped chain = wrap_middleware(mw, chain) else: def make_step(middleware: Callable, next_step: Callable) -> Callable: def step(req: Request) -> Response: return middleware(req, next_step) return step chain = make_step(mw, chain) # 3. Запускаем цепочку с обработкой исключений try: response = chain(request) except Exception as e: for hook in self._exception_hooks: hook_response = hook(request, e) if hook_response is not None: response = hook_response break else: raise # 4. Хуки после контроллера (модификация ответа) for hook in self._after_request_hooks: response = hook(request, response) # 5. Если профайлер включён, завершаем замер и логируем отчёт if self.profiler_enabled and profiler_context: profiler_context.finish() logger.info( f"Профайлер запроса {request.get_method()} {request.get_path()}:\n{profiler_context.report()}" ) return response def terminate(self, request: Request, response: Response) -> None: """ Вызывается после отправки ответа клиенту (для фоновых задач). """ for hook in self._terminate_hooks: hook(request, response) # ========== Внутренняя обработка запроса ========== def _process_request(self, request: Request) -> Response: """ Основная логика обработки запроса (без глобальных middleware и хуков). """ method = request.get_method() path = request.get_path() query_params = request.get_query_params() body = request.get_body() # 1. Поиск маршрута result = self.router.match(path, method) if result is None: raise HttpNotFound(f"Маршрут {method} {path} не найден") controller, route_params, group_middlewares = result # 2. Устанавливаем параметры маршрута в Request request.set_route_params(route_params) # 3. Создание экземпляра контроллера if isinstance(controller, tuple) and len(controller) == 2: controller_class, method_name = controller try: instance = self.container.get(controller_class) # type: ignore[arg-type] except Exception as e: return self.response_class( content={"error": f"Ошибка создания контроллера: {str(e)}"}, status=500, headers={"Content-Type": "application/json"}, ) if not hasattr(instance, method_name): return self.response_class( content={"error": f"Метод {method_name} не найден"}, status=500, headers={"Content-Type": "application/json"}, ) controller_method = getattr(instance, method_name) else: controller_method = controller # 4. Разрешение аргументов через ArgumentResolver kwargs = self.argument_resolver.resolve( controller_method=controller_method, request=request, route_params=route_params, query_params=query_params, body=body, method=method, ) # 5. Создаём цепочку групповых middleware -> контроллер def call_controller_with_kwargs(req: Request) -> Response: try: response = controller_method(**kwargs) except Exception as e: return self.response_class( content={"error": f"Ошибка выполнения контроллера: {str(e)}"}, status=500, headers={"Content-Type": "application/json"}, ) # Преобразование ответа в Response if isinstance(response, self.response_class): return response elif isinstance(response, tuple): if len(response) == 2: data, status = response return self.response_class( content=data, status=status, headers={"Content-Type": "application/json"}, ) elif len(response) == 3: data, status, headers = response return self.response_class( content=data, status=status, headers=headers ) else: return self.response_class( content={"error": "Некорректный формат ответа"}, status=500, headers={"Content-Type": "application/json"}, ) else: return self.response_class( content=response, status=200, headers={"Content-Type": "application/json"}, ) chain = call_controller_with_kwargs for mw in reversed(group_middlewares): if self.profiler_enabled: def wrap_group_middleware( middleware: Callable, next_step: Callable ) -> Callable: def wrapped(req: Request) -> Response: start = time.time() response = middleware(req, next_step) duration = time.time() - start context = req.get_attr("_profiler_context") if context: name = getattr(middleware, "__name__", str(middleware)) context.add_middleware(name, duration) return response return wrapped chain = wrap_group_middleware(mw, chain) else: def make_group_step( middleware: Callable, next_step: Callable ) -> Callable: def step(req: Request) -> Response: return middleware(req, next_step) return step chain = make_group_step(mw, chain) return chain(request)