Overridable methods#
Handlers and rules decide what each node becomes. The methods on this page decide everything around them: the starting settings, the layout of lines and blocks, and the final output. They are listed roughly in the order you are likely to need them.
Method |
Override it to |
Built-in overrides |
|---|---|---|
produce the final output |
every built-in renderer |
|
write the output to a file |
Word, Excel |
|
lay out lines and blocks |
LaTeX, Markdown, HTML |
|
set default settings |
LaTeX, HTML |
|
lay out parameter grids |
LaTeX, HTML |
|
lay out multi-line ( |
LaTeX, HTML |
|
change how the top level is walked |
Excel |
|
change the joining walk itself |
(none) |
All are defined on BaseRenderer.
complete(tree, base_context)#
Called once, after rendering, with the whole render tree. Its return
value is what the interface returns. BaseRenderer.complete returns the tree unchanged.
For a text format, join and finish:
def complete(self, tree, base_context):
body = self.join(tree, base_context)
return f"<section class='calc'>{body}</section>"
For anything else, walk the tree yourself: the Excel renderer turns its rows into a
workbook (ExcelRenderer.complete),
the Word renderer writes its tree into a .docx
(WordRenderer.complete),
and the structured data example flattens the tree into dicts.
Built-in versions:
ASCIIRenderer.complete,
LatexRenderer.complete,
HTMLRenderer.complete.
join_item(item, depth, base_context)#
Called by join for each item of the tree, with its nesting depth. Returns a list of
strings that are concatenated into the output. This is where a renderer’s layout lives.
The base version handles the tree shapes: a string is a whole line, a
list of strings is one line’s parts (joined with context.space), and [header, body] is
a block (header at this depth, body one level deeper, indented with context.indent).
Every line ends with context.newline.
Override it to change how lines or blocks are written, and hand everything else back to the parent:
def join_item(self, item, depth, base_context):
if item and isinstance(item, list) and isinstance(item[-1], list):
header, body = item[0], item[-1]
if isinstance(header, str) and header.strip():
inner = "".join(self.join_items(body, depth + 1, base_context))
return [f"<details><summary>{header}</summary>{inner}</details>"]
return super().join_item(item, depth, base_context)
Built-in versions show three styles:
HTML (nested <div>s),
LaTeX (paragraphs, math groups,
list environments), and
Markdown (blockquotes).
create_context(**kwargs)#
Called once per render with the configuration as keyword arguments. Returns the
BaseRenderContext. Override it to give your renderer its own defaults; merge them
under kwargs so the configuration can still override them:
from handcalcs.renderers.base import BaseRenderContext, RenderContext
def create_context(self, **kwargs):
defaults = {"lpar": "\\left(", "rpar": "\\right)", "indent": ""}
return BaseRenderContext(RenderContext(**(defaults | kwargs)), RenderContext.sparse())
See The render context.
format_param_grid(rows, base_context)#
Called by the params_block handler. rows is a list of rows; each cell is the rendered
parameter as a list of parts, [identifier, "=", value] plus a comment for # hc: -h
grids, or None to pad the last row. Return the grid’s rendered form, usually one string.
The base version pads columns to line up the = signs in plain text. The LaTeX version
builds an aligned group
(LatexRenderer.format_param_grid),
the HTML version a <table>
(HTMLRenderer.format_param_grid).
format_calc_grid(rows, base_context)#
Called by the calc_line handler for a multi-line (# hc: -m) calculation. rows is a
list of [identifier, "=", value] triples: the first has the target, the rest an empty
identifier. Return the rendered calculation, usually one string.
render_root(root, base_context)#
Walks the top-level nodes, running root:pre and root:post rules around them, and
returns the tree as a list. Override it when the top level needs different handling, such
as flattening: the Excel renderer flattens every statement into one list of rows
(ExcelRenderer.render_root).
For adding a title or a footer, a root:pre or root:post rule is simpler.
to_file(rendered, path)#
Called by HandCalcs when a destination is given, with what complete returned. It
writes the file and returns its pathlib.Path.
The base version writes a string as UTF-8, between the renderer’s preamble and
postamble attributes (both empty unless you set them), and raises TypeError for
anything else. So a text renderer usually only sets the two attributes, as the LaTeX
and HTML renderers do:
class RstRenderer(LatexRenderer):
preamble = ".. Generated by handcalcs\n\n"
postamble = ""
Override to_file when complete returns an object that saves itself. The Word and
Excel renderers do:
def to_file(self, rendered, path):
path = pathlib.Path(path)
rendered.save(path)
return path
Source: BaseRenderer.to_file,
WordRenderer.to_file.
join(tree, base_context) and join_items(items, depth, base_context)#
join calls join_items(tree, 0, ...) and concatenates the result; join_items calls
join_item for each item. You rarely need to override them; call them from your own
complete and join_item.
Renderer-specific hooks#
The LaTeX family splits its layout into smaller methods you can override instead of the
whole join_item:
Method |
Does |
|---|---|
|
Prefixes a prose paragraph ( |
|
Wraps joined rows in |
|
Joins a |
|
Joins one line’s parts into an |
|
Blockquotes a block body |
The reStructuredText example overrides math_group and
paragraph.
Class attributes#
Attribute |
Purpose |
|---|---|
|
Registers the renderer for selection by name (config, |
|
Source for |
|
Text |
|
The registration tables. |
Next: The render tree.