Builders overviewο
Last Updated: 2026-06-02 Status: π’ APPROVATO β allineato al contratto v0.7.0.
A builder is a Python class that defines a grammar for a structured document: HTML, SVG, CSS, or any user-defined dialect.
The three objectsο
The framework is built around three concrete classes:
Builder (
HtmlBuilder,SvgBuilder,CssBuilder, β¦) β declares the grammar via decorators. No runtime state. No rendering logic.Renderer (
HtmlRenderer,SvgRenderer,CssRenderer, β¦) β walks the source bag and emits a string. One renderer class per grammar, one renderer instance per mode.Handler (
HtmlBuilderHandler,SvgBuilderHandler,CssBuilderHandler, β¦) β the engine. Owns one builder instance, one source bag, and a mode-keyed dict of render targets. Drives the lifecycle.
The user subclasses the handler and implements main(self, root).
The two phasesο
The handlerβs lifecycle has two phases, called explicitly:
page = MyPage()
page.create() # main(source) populates self.source
page.render() # serializes source
Phase |
Method |
What it does |
|---|---|---|
Create |
|
Calls |
Render |
|
Dispatches to the renderer registered for |
The source bag is inspectable as page.source after create().
Grammar declarationο
A builder declares its grammar via decorators on methods:
from genro_builders.builder import BagBuilderBase, element
class MyBuilder(BagBuilderBase):
@element(sub_tags='body')
def html(self): ...
@element(sub_tags='h1,p[]')
def body(self): ...
@element(sub_tags='') # leaf (void element)
def br(self): ...
See Decorators for the full list.
Handler subclassingο
The user defines the handler subclass by overriding main:
from genro_builders.contrib.html import HtmlBuilderHandler
class CustomerPage(HtmlBuilderHandler):
def main(self, root):
root.body().h1("Customer page")
HtmlBuilderHandler has its builder_class already set to
HtmlBuilder. The user only writes main.
What lives whereο
Concern |
Owned by |
|---|---|
Grammar (tags, validation, schema) |
Builder |
Source bag (the recipe) |
Handler |
Rendering (string output) |
Renderer (one per mode, instantiated lazily by handler) |
Render targets (file, stream, buffer; one per mode) |
Handler |
Node lookup by id |
Handler ( |
This separation is fixed by the architecture contract (sections
BLD.3 / HND.3). See roadmap/architecture-contract.md.
What is here, and what is nextο
Already implemented:
Pull-based binding β
^pointer/=pointer/${name}resolved at render time (contractDAT.2).Data-elements β
data_setter,data_formula,data_controller(plain@elementmarked as data) run at first calculation duringcreate()and recompute in a single wave when a dependency mutates (DAT.4). See Decorators.Push reactivity, Level 0 β inside
with handler.live(target):every mutation triggers a full re-render to the target (RX.1).
Designed but not yet implemented:
Data-element cascade (slice 2) β multi-wave re-firing of dependent data-elements. See the
RXarea androadmap/reactivity/data-elements.md.Finer-grained push reactivity β per-attribute updates, SRC/DATA granularity (
RX).Multi-builder orchestration (
BuilderSuite). SeeSUITEarea of the contract.