Rendering internals#
base holds the rendering engine and the base handler for every
node type. The public side of it is documented in the
renderer-building guide; this page covers the parts you
meet when changing the engine or the base handlers.
Dispatch#
BaseRenderer.rendersends the root torender_rootand everything else torender_node.render_nodewraps a raw Python value (anything without atypeattribute) in a node with_reifyfirst. That is how aName’s value is formatted by theconstanthandler, and how a list value becomes aListnode whose elements are each formatted.Registration is routed by
_route_registration; the tables are copied per subclass in__init_subclass__and per instance in__init__.The registry of renderer names is
_RENDERER_REGISTRY, filled in__init_subclass__.BaseRendererregisters itself explicitly at the end of the module, since__init_subclass__doesn’t run for the class that defines it.
Calculation lines#
render_calcline is the most involved
handler. In order:
Render the comment first (an inline command replaces the line context).
If
ignoreis set, reset the line context and return"".Build the columns allowed by
mode: assignment targets (sym), symbolic expression (sym), numeric expression (num), result (targets innum). A param line skips the two expression columns.Drop any column equal to the one before it, so
q = ydoesn’t readq = y = 3 = 3.If
multilineis set and there is something to break, build[id, "=", value]rows and returnformat_calc_grid(rows).Otherwise interleave the columns with
context.assign, append the comment, reset the line context and return the list of parts.
Whether a line is a parameter is decided before this by the calc_line:pre rule
toggle_param_line: a scalar literal, a
negated literal or a collection of literals, or a right-hand side wrapped in parentheses
(pars_nesting).
Precedence and parentheses#
The parser does not keep source parentheses; the renderer adds them where precedence
needs them. infix_binop compares each
operand’s precedence with the operator’s (OP_PRECEDENCE):
a left operand that binds looser is wrapped;
a right operand that binds looser, or equally for a non-commutative operator (
a - (b - c)), is wrapped;an operand that renders with a leading sign is wrapped (
7·(-3)), as is a signed base of a power ((-2)²).
infix_boolop and
render_unary_op follow the same table;
unary operators sit between multiplication and power (UNARY_PRECEDENCE = 2.5).
Renderers that draw some operators structurally (LaTeX fractions) override those handlers
and skip the parentheses that the structure makes unnecessary.
Blocks#
render_block_body renders every block
as [header, body]: the header from render_header (the header:{type} handler) wrapped
in the node’s header_pre/header_post, and the body with body_pre/body_post added
as first and last items. The LaTeX renderer uses body_pre/body_post as sentinels to
mark a math group (calcs_block_pre_post).
render_elifblock renders only the true
branch.
Joining#
join_item documents the three
tree shapes; the built-in renderers’ join_item overrides each start from the same
dispatch. Keep the shapes stable: every renderer depends on them.
Comments and Jinja#
The
preruleinterpolate_jinja_stringsrenders the content of headings, comment lines and inline comments as a Jinja template againstcontext.global_ns. It usesDebugUndefined, so an unknown name is left as{{ name }}instead of raising.