/
liquid-g
/
liquid-code
Обзор
Документация
Войти
/
liquid-g
/
liquid-code
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop-0.4
src/liquidcode/kernel/kernel.py
310 строк
12 KB
User
0.4.20 - refactor: централизация обработки исключений в ErrorHandlingMiddleware (T-003)
07 июл 2026, 22:54
07 июл 2026, 22:54
26d8fe2
Код
Авторство
О чём код?
""" Модуль ядра приложения. Содержит класс Kernel, который координирует обработку запроса: - маршрутизация - middleware (глобальные и групповые) - хуки (до/после запроса, завершения) - разрешение аргументов контроллера - вызов контроллера - профилирование - события (request, response, terminate) Обработка исключений централизована в ErrorHandlingMiddleware. """ import logging from typing import Callable, List, Optional, Type from .chain_builder import build_middleware_chain from .controller_resolver import resolve_controller from .exceptions import HttpNotFound from .options_handler import handle_options from .response_normalizer import normalize_response from ..container import Container from ..contracts import Request, Response from ..event_dispatcher import EventDispatcher from ..events import RequestEvent, ResponseEvent, TerminateEvent from ..http import HttpResponse from ..profiler import ProfilerContext from ..routing import Router logger = logging.getLogger(__name__) class Kernel: """ Ядро приложения. Принимает Request, прогоняет через цепочку middleware, находит и вызывает контроллер, возвращает Response. Исключения, возникшие на любом этапе, перехватываются ErrorHandlingMiddleware, которая должна быть первой в цепочке middleware. """ def __init__( self, router: Router, container: Container, response_class: Optional[Type[Response]] = None, profiler_enabled: bool = False, event_dispatcher: Optional[EventDispatcher] = None, debug: bool = False, ) -> None: """ Инициализирует ядро. Args: router: Объект роутера. container: DI-контейнер. response_class: Класс ответа (по умолчанию HttpResponse). profiler_enabled: Включить профилировщик. event_dispatcher: Диспетчер событий. debug: Режим отладки. """ self.router = router self.container = container self.response_class = response_class or HttpResponse self.profiler_enabled = profiler_enabled self.event_dispatcher = event_dispatcher or EventDispatcher() self.debug = debug # Ленивая инициализация ArgumentResolver self._argument_resolver = None # Списки middleware и хуков self._middlewares: List[Callable] = [] self._before_request_hooks: List[Callable] = [] self._after_request_hooks: List[Callable] = [] self._exception_hooks: List[Callable] = [] # сохраняются для аудита self._terminate_hooks: List[Callable] = [] @property def argument_resolver(self): """Лениво создаёт ArgumentResolver с доступом к контейнеру.""" if self._argument_resolver is None: from .argument_resolver import ArgumentResolver self._argument_resolver = ArgumentResolver(self.container) return self._argument_resolver # ---------- Константы ---------- MAX_MIDDLEWARE_DEPTH = 100 # ---------- Middleware API ---------- def add_middleware(self, middleware: Callable) -> None: """ Добавляет глобальный middleware с проверкой глубины. Args: middleware: Экземпляр middleware (callable). Raises: RuntimeError: Если превышен лимит глубины (защита от рекурсии). """ if len(self._middlewares) >= self.MAX_MIDDLEWARE_DEPTH: raise RuntimeError( f"Достигнут предел глубины middleware " f"({self.MAX_MIDDLEWARE_DEPTH}). " "Это может привести к RecursionError." ) self._middlewares.append(middleware) # ---------- Хуки ---------- def add_before_request(self, hook: Callable) -> None: """Добавляет хук, выполняемый до обработки запроса.""" self._before_request_hooks.append(hook) def add_after_request(self, hook: Callable) -> None: """Добавляет хук, выполняемый после обработки запроса.""" self._after_request_hooks.append(hook) def add_exception(self, hook: Callable) -> None: """ Добавляет хук для обработки исключений. Хуки вызываются из ErrorHandlingMiddleware после перехвата исключения, но до формирования ответа об ошибке. """ self._exception_hooks.append(hook) def add_terminate(self, hook: Callable) -> None: """Добавляет хук, выполняемый после отправки ответа клиенту.""" self._terminate_hooks.append(hook) # ---------- Основной метод обработки ---------- def handle(self, request: Request) -> Response: """ Основной метод обработки запроса. Последовательность: 1. Событие RequestEvent. 2. Хуки before_request. 3. Запуск глобальной цепочки middleware. 4. Внутренняя обработка (_process_request) или выход из middleware. 5. Событие ResponseEvent. 6. Хуки after_request. 7. Завершение профилировщика. Исключения перехватываются ErrorHandlingMiddleware, которая должна быть добавлена в цепочку middleware. Args: request: Объект запроса. Returns: Response: Объект ответа. """ # 1. Событие: request req_event = RequestEvent(request) self.event_dispatcher.dispatch(req_event) if req_event.response is not None: return req_event.response # 2. Хуки before_request for hook in self._before_request_hooks: response = hook(request) if response is not None: return response # 3. Профайлер (включается, если включен) profiler_context = None if self.profiler_enabled: profiler_context = ProfilerContext() request.set_attr('_profiler_context', profiler_context) # 4. Строим глобальную цепочку middleware def call_controller(req: Request) -> Response: # Этот вызов будет обёрнут middleware return self._process_request(req) global_chain = build_middleware_chain( call_controller, self._middlewares, self.profiler_enabled ) # Запускаем цепочку. # Исключения не перехватываются здесь — они уходят в ErrorHandlingMiddleware. response = global_chain(request) # Проверка в debug-режиме, что каждый middleware вернул Response if self.debug and response is not None and not isinstance(response, Response): raise TypeError( f"Middleware '{response.__class__.__name__}' должен " f"возвращать Response, а вернул {type(response).__name__}" ) # 5. Событие: response resp_event = ResponseEvent(request, response) self.event_dispatcher.dispatch(resp_event) response = resp_event.response # 6. Хуки after_request for hook in self._after_request_hooks: response = hook(request, response) # 7. Профайлер (финиш) if self.profiler_enabled and profiler_context: profiler_context.finish() logger.info( f"Профайлер запроса {request.get_method()} {request.get_path()}:\n" f"{profiler_context.report()}" ) return response # ---------- Завершение запроса ---------- def terminate(self, request: Request, response: Response) -> None: """ Вызывается после отправки ответа клиенту. Args: request: Объект запроса. response: Объект ответа. """ term_event = TerminateEvent(request, response) self.event_dispatcher.dispatch(term_event) for hook in self._terminate_hooks: hook(request, response) # ---------- Профайлер ---------- def enable_profiler(self) -> None: """Включает профилировщик для всех последующих запросов.""" self.profiler_enabled = True logger.info("Профилировщик включён") # ---------- Внутренняя обработка (без глобальных middleware) ---------- def _process_request(self, request: Request) -> Response: """ Основная логика обработки запроса без глобальных middleware. Выполняет: - Обработку OPTIONS-запросов. - Поиск маршрута. - Разрешение контроллера. - Разрешение аргументов. - Вызов контроллера через групповые middleware. Исключения не перехватываются — они идут вверх по цепочке. Args: request: Объект запроса. Returns: Response: Объект ответа. Raises: HttpNotFound: Если маршрут не найден. Другие исключения: пробрасываются в ErrorHandlingMiddleware. """ method = request.get_method() path = request.get_path() query_params = request.get_query_params() # 1. OPTIONS-запрос (CORS preflight) if method.upper() == 'OPTIONS': return handle_options(request, self.router) # 2. Поиск маршрута result = self.router.match(path, method) if result is None: raise HttpNotFound(f"Маршрут {method} {path} не найден") controller, route_params, group_middlewares = result request.set_route_params(route_params) # 3. Разрешение контроллера (без try/except) controller_method, _ = resolve_controller(controller, self.container) # 4. Разрешение аргументов kwargs = self.argument_resolver.resolve( controller_method=controller_method, request=request, route_params=route_params, query_params=query_params, method=method, ) # 5. Вызов контроллера через групповые middleware def call_controller_with_kwargs(req: Request) -> Response: # Не перехватываем исключения — они идут вверх result = controller_method(**kwargs) return normalize_response(result, self.response_class) group_chain = build_middleware_chain( call_controller_with_kwargs, group_middlewares, self.profiler_enabled ) response = group_chain(request) # Проверка в debug-режиме if self.debug and response is not None and not isinstance(response, Response): raise TypeError( f"Middleware '{response.__class__.__name__}' должен " f"возвращать Response, а вернул {type(response).__name__}" ) return response