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