Dispatcher
VisitorDispatcher is the entry point for evaluating expression trees. It selects a visitor by a concrete node type, runs optional extensions and middlewares, and memoizes the results for each node within a single visit call.
dynamic_expressions.dispatcher.VisitorDispatcher
Resolve and evaluate expression nodes with registered visitors.
The dispatcher selects a visitor by a concrete node type, runs optional
extensions and middlewares, and caches the results for each node within a single visit call.
Example
__init__(visitors, extensions=(), middlewares=())
Configure visitors and optional hooks around node evaluation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
visitors
|
Mapping[type[Node], Visitor[Any, Any]]
|
Mapping from node class to the visitor that evaluates it. |
required |
extensions
|
Sequence[OnVisitExtension[Context]]
|
Lifecycle hooks are entered before each node is visited. |
()
|
middlewares
|
Sequence[OnVisitMiddleware[Context]]
|
Middleware chain wrapping the selected visitor. |
()
|
visit(node, context)
async
Evaluate an expression tree against context.
Accepts either a Node or an
Expression wrapper.
Results for individual nodes are memoized for the lifetime of this
call via an internal ExecutionContext.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
Node | Expression[Any]
|
Root node or expression to evaluate. |
required |
context
|
Context
|
Input data passed to visitors and nested dispatches. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The evaluated result of |
Configuration
The dispatcher accepts three optional hook layers:
| Parameter | Type | Purpose |
|---|---|---|
visitors |
Mapping[type[Node], Visitor] |
Required — maps each node class to its evaluator |
extensions |
Sequence[OnVisitExtension] |
Async context managers are entered before each node is visited |
middlewares |
Sequence[OnVisitMiddleware] |
Middleware chain wrapping the selected visitor |
from dynamic_expressions.dispatcher import VisitorDispatcher
from dynamic_expressions.nodes import LiteralNode, BinaryExpressionNode
from dynamic_expressions.visitors import LiteralVisitor, BinaryExpressionVisitor
from dynamic_expressions.types import EmptyContext
dispatcher = VisitorDispatcher[EmptyContext](
visitors={
LiteralNode: LiteralVisitor(),
BinaryExpressionNode: BinaryExpressionVisitor(),
},
)
node = BinaryExpressionNode(
operator="+",
left=LiteralNode(value=1),
right=LiteralNode(value=2),
)
result = await dispatcher.visit(node, None) # 3
visit accepts either a Node or an Expression wrapper.
Execution flow
- Extensions run first (for example, Redis cache lookup).
- If the node is already in
ExecutionContext.cache, the cached value is returned. - Middlewares wrap the visitor call.
- The visitor evaluates the node, using
dispatchfor children. - The result is stored in
ExecutionContext.cachefor the rest of thisvisitcall.
dynamic_expressions.types.ExecutionContext
dataclass
Per-call memoization means the same node instance evaluated twice within one visit is computed only once. This is separate from Redis caching via extensions.
Related topics
- Architecture — full evaluation pipeline
- Extensions — cross-cutting hooks (caching, metrics)
- Middlewares — wrap individual visitor calls