Settings and the render context#
While it renders, a renderer reads its settings from a render context: a simple object whose attributes are the settings. Changing a context field is the lightest way to change the output, and needs no subclass.
Fields the built-in renderers read#
Field |
Default |
Meaning |
|---|---|---|
|
|
|
|
|
Which steps to show: |
|
|
The symbol between the steps of a calculation. |
|
|
Break calculations over several lines ( |
|
|
Show target = result only ( |
|
|
Columns in a parameter grid ( |
|
|
Leave lines out of the output ( |
|
|
The separator between the parts of a line. |
|
|
The line separator. |
|
|
One level of indentation for block bodies. |
|
|
Parentheses added for operator precedence and around comments. |
|
|
|
|
|
Display specs for names, e.g. |
|
|
Split a Greek name before an uppercase letter or digit ( |
|
|
A list, tuple, set or dict with more items shows only its first and last |
|
|
The items kept at each end of a shortened collection. |
The LaTeX and Markdown renderers set space, newline, indent, lpar and rpar
themselves (for example lpar is \left(). The HTML renderer ignores indent; its
indentation comes from CSS.
Two more fields are set by the interface: global_ns, the namespace the values come
from (used for {{ }} in comments), and, during rendering, current_mode ("sym" or
"num"). See the render context in the
renderer-building guide for how the context works internally.
Four ways to set a field#
For every render in the session (or permanently, with save_config()):
import handcalcs
handcalcs.set_option("assign", ":=")
For part of one calculation, with the assignment form of a comment command. It applies to every line after it:
a = 2
b = 3
# hc: assign = ":="
c = a + b
d = c * 2
a = 2
b = 3
c := a + b := 2 + 3 := 5
d := c * 2 := 5 * 2 := 10
For one line, at the end of that line. Note that format_code applies to the
substituted values as well as the result:
a = 85
b = 110
c = a / b # hc: format_code = ".2f"
d = a / b
a = 85
b = 110
c = a / b = 85.00 / 110.00 = 0.77
d = a / b = 85 / 110 = 0.77273
For every call of a decorated function:
@handcalc(ASCIIRenderer(), render_context={"format_code": ".2f"})
def calc(a, b): ...
Caution
In handcalcs 2.0, setting space, newline, indent, lpar or rpar with
set_option makes the LaTeX and Markdown renderers raise a TypeError, because they
set those fields themselves. Change them in a subclass’s create_context instead (see
overridable methods).
Your own fields#
Any name can be used. The built-in renderers ignore names they don’t use, and a rule
you write can read them from the context. This makes a rule switchable from a comment.
This renderer marks lines with a note given by a flag field:
"""A custom context field read by a rule: '# hc: flag = "..."' marks lines."""
from handcalcs.renderers import ASCIIRenderer
class FlagASCIIRenderer(ASCIIRenderer):
"""ASCII output that can mark lines with a note."""
@FlagASCIIRenderer.register("calc_line:post")
def flag_line(renderer, rendered, node, base_context):
# A line's own commands are reset before 'post' rules run, so read them from the line.
commands = getattr(node.comment, "commands", None) or {}
flag = commands.get("flag", getattr(base_context.current, "flag", None))
if not flag:
return rendered
if isinstance(rendered, list):
return rendered + [f"<-- {flag}"]
return rendered + f" <-- {flag}" # a multi-line (# hc: -m) calc is one string
M_f = 85; M_r = 110; V_f = 40; V_r = 35
u_M = M_f / M_r
u_V = V_f / V_r # hc: flag = "governs"
# hc: flag = "check later"
u_T = 0.4 * u_V
M_f = 85 M_r = 110 V_f = 40
V_r = 35
u_M = M_f / M_r = 85 / 110 = 0.77273
u_V = V_f / V_r = 40 / 35 = 1.1429 <-- governs
u_T = 0.4 * u_V = 0.4 * 1.1429 = 0.45714 <-- check later
Read optional fields with getattr(context, name, default), since a field only exists
once something has set it. A field set at the end of a line is in
base_context.current while the line’s parts render, but is reset before the line’s
post rules run; a post rule reads it from node.comment.commands, as above.
Next: Styling the HTML output.