# Copyright 2025 Softwell S.r.l.
# Licensed under the Apache License, Version 2.0
"""HandlerExecutor — ASGI app that executes a pre-resolved router node.
Used as innermost app in per-app middleware chains. The Dispatcher sets
scope["_handler_node"] before invoking the per-app chain.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from genro_routes import is_result_wrapper
from genro_toolbox.smartasync import smartasync
if TYPE_CHECKING:
from .server import AsgiServer
from ..types import Receive, Scope, Send
__all__ = ["HandlerExecutor"]
[docs]
class HandlerExecutor:
"""ASGI app that executes a pre-resolved router node.
Expects scope["_handler_node"] set by Dispatcher before invocation.
Retrieves the current request from server.request_registry.
"""
__slots__ = ("server",)
[docs]
def __init__(self, server: AsgiServer) -> None:
"""Args:
server: Parent AsgiServer instance (provides request_registry).
"""
self.server = server
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
"""Execute pre-resolved handler node and send ASGI response.
Args:
scope: ASGI scope with "_handler_node" set by Dispatcher.
receive: ASGI receive callable.
send: ASGI send callable.
"""
node = scope["_handler_node"]
request = self.server.request_registry.current
assert request is not None
result = await smartasync(node)(**dict(request.query))
if is_result_wrapper(result):
metadata: dict[str, Any] = {**node.metadata, **result.metadata}
request.response.set_result(result.value, metadata)
else:
request.response.set_result(result, node.metadata)
await request.response(scope, receive, send)
if __name__ == "__main__":
pass