Dispatcher
Dispatcher is the resolver for a node.
The dispatcher takes a node and a context, where the node is an expression, the context is the input parameters for the expression. As an example, we can implement a role model and pass the user as the context.
You can easily override the behavior for a node by overriding the visitor for the node in __init__ in VisitorDispatcher.
Source code in dynamic_expressions/dispatcher.py
| class VisitorDispatcher[Context: EmptyContext]:
def __init__(
self,
visitors: Mapping[type[Node], Visitor[Any, Context]],
extensions: Sequence[OnVisitExtension[Context]] = (),
middlewares: Sequence[OnVisitMiddleware[Context]] = (),
) -> None:
self._visitors = visitors
self._on_visit_exts = extensions
self._middlewares = middlewares
async def visit(
self,
node: Node,
context: Context,
) -> Any: # noqa: ANN401
execution_context = ExecutionContext()
return await self._visit(
node=node,
context=context,
execution_context=execution_context,
)
async def _visit(
self,
node: Node,
context: Context,
execution_context: ExecutionContext,
) -> Any: # noqa: ANN401
async with AsyncExitStack() as stack:
for ext in self._on_visit_exts:
await stack.enter_async_context(
ext.on_visit(
node=node,
provided_context=context,
execution_context=execution_context,
)
)
if node in execution_context.cache:
return execution_context.cache[node]
visitor = self._visitors[type(node)]
middleware_stack = MiddlewareStack(
middlewares=self._middlewares,
visitor=visitor,
dispatch=functools.partial(
self._visit,
execution_context=execution_context,
),
)
result = await middleware_stack.call(
node=node,
context=context,
)
execution_context.cache[node] = result
return result
|