Skip to content

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
dispatcher = VisitorDispatcher(
    visitors={
        LiteralNode: LiteralVisitor(),
        BinaryExpressionNode: BinaryExpressionVisitor(),
    },
)
result = await dispatcher.visit(expression_node, context)

__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 node.

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

  1. Extensions run first (for example, Redis cache lookup).
  2. If the node is already in ExecutionContext.cache, the cached value is returned.
  3. Middlewares wrap the visitor call.
  4. The visitor evaluates the node, using dispatch for children.
  5. The result is stored in ExecutionContext.cache for the rest of this visit call.

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.