Skip to main content

genro-builders

Builder system for genro-bag — domain-specific grammars, rendering, and runtime data binding via pointers, built on top of bag data structures.

Installation

pip install genro-builders

Quick start

A page is a builder: subclass the dialect and implement main.

from genro_builders.contrib.html import HtmlBuilder


class HelloPage(HtmlBuilder):
    def main(self, root):
        body = root.body()
        body.h1("Hello World")
        body.p("My first page with genro-builders.")


page = HelloPage()
page.create()
print(page.render(pretty=True))

The lifecycle is two-phase, both on the builder:

  • create() runs setup(self.data) (seed the data), then the user-defined main(self.source) that populates the source Bag through the dialect's grammar API, then the first calculation of the data-elements.
  • render(mode=None, target=None, **opts) drives the universal walk on the source and produces the dialect's output. Default mode comes from the dialect; default target returns the string. It composes two steps you can also call on their own: materialize(mode) walks and keeps the result in materialized[mode], finalize delivers it.

Rendering does not validate. validate_source() reports the nodes whose minimum child cardinality is unmet — (fullpath, [missing tags]) per node, empty when complete — when the author asks for it.

Dialects (contrib)

The package ships with reference dialects, each as a <Dialect>Builder grammar:

  • HTML5 — genro_builders.contrib.html.HtmlBuilder (HTML5 grammar with CSS kwargs)
  • SVG — genro_builders.contrib.svg.SvgBuilder
  • CSS — genro_builders.contrib.css.CssBuilder
  • XSD — genro_builders.xml (codegen: an XSD schema becomes a <Dialect>Builder you commit and import)

Mixed-dialect documents are supported via sub-builders: a grammar element marked _meta['subbuilder'] switches the active dialect from that node down, and the render walk picks the right renderer per node. HTML hosts SVG with body.svg(...); SVG hosts HTML with svg.html(...), wrapped in <foreignObject> automatically.

from genro_builders.contrib.html import HtmlBuilder


class Badge(HtmlBuilder):
    def main(self, root):
        body = root.body()
        svg = body.svg(viewBox="0 0 200 80", width=200, height=80)
        svg.rect(x=0, y=0, width=200, height=80, fill="#2c3e50")


page = Badge()
page.create()
print(page.render())

Architecture (one-paragraph map)

A builder declares the grammar of a dialect (decorators @element, @abstract; the three data-elements dataSetter / dataFormula / dataController are plain @element marked as data) and is also the document: it owns name, source, create()/render(), and exposes its renderers as renderer_<mode> properties. It also owns its datastore: one FLAT Bag, builder.data, with absolute paths and no leading segment — reachable from any node as node.data. As the source, it is the content node _root_ of a private wrapper, _dataroot, so the datastore is a tree. A renderer is responsible for one mode: the universal walk produces fragments via dialect-specific rendered_item, then finalize ships the result to the target.

The builder also declares the class of its Source: _source_class (default SourceBag), redefinable per subclass. The Source declares the class of its nodes (_node_class). Branches follow the class of their parent bag. See the Source class.

Runtime data binding (pull-based)

Attribute values and node text can carry pointers and templates, resolved at render time:

  • ^path — pointer declared as meant to follow the datum
  • =path — passive pointer (read only)

Both resolve the same way in a static render: the difference is the author's declaration of intent, which a reactive engine would act on.

  • ^path:{}px — explicit text suffix on an attribute pointer.
  • ^path:=!{} — expression suffix, preserved but inert in static Python output.
  • ${name} — ordinary literal text; referenced attributes are never consumed.

See the pointer suffix and dtype migration guide.

from genro_builders.contrib.html import HtmlBuilder


class Page(HtmlBuilder):
    def setup(self, data):
        data.set_item("greeting", "Hello")

    def main(self, root):
        root.body().h1("^greeting")


page = Page()
page.create()                  # setup + main + the data-elements
print(page.render())
# ...<h1>Hello</h1>...

# Mutate the data and re-render: pull-based, no auto-render.
page.data.set_item("greeting", "Ciao")
print(page.render())
# ...<h1>Ciao</h1>...

Re-render is the whole reactivity model here: change the data, render again. Fine-grained reactivity is a separate engine, still under design — see the RX area of the contract.

The companion API on each source node:

  • node.abs_datapath(path) — turn a relative path into an absolute one in the datastore
  • node.get_relative_data(path) / node.set_relative_data(path, value) — read/write the datastore relative to the node
  • node.SET / GET / PUT / FIRE — DSL macros over the same two entry points; the fired/reason flags they carry travel through the genro-bag subscribe pipeline, and nothing in this package acts on them (vocabulary for the future reactive engine)

Render target

page = HelloPage()
page.create()

# Return a string (default)
text = page.render()

# Write to a path
page.render(target="out.html")

# Push to a file-like or invoke a callable
import io
page.render(target=io.StringIO())
page.render(target=print)

# Register a default target (per mode), then render to it
page.set_render_target("out.html")
page.render()

Examples

Runnable tutorials under src/genro_builders/contrib/<dialect>/examples/, grouped by scenario:

  • HTML — no_data/ (grammar, styling, sub-builders, validation, render modes), with_data/ (pointers, datapath, presentation), with_logic/ (data-elements)
  • SVG — 01_introduction, badge_sheet, bar_chart, and more
  • CSS — 01_introduction
  • XSD — 01_person_schema; XSLT — 01_sitemap_to_html

Each example ships a runnable .py, a readme.md, and the rendered output. The test suite runs them all (tests/test_examples.py).

Documentation

Downstream

genro-builders is a generic engine: the grammars, the renderers, and the pull-based data binding know nothing about who consumes them. The source carries no reference to any downstream layer — this is the one place that names them. Known consumers in the Genro ecosystem:

  • genro-ws-web — WebSocket-driven reactive SPA framework (the production widget kit and the push transport live here)
  • genro-office — Office document generation (Word and Excel builders)
  • genro-print — print and PDF generation system
  • genro-textual — Textual UI framework for Bag-driven applications
  • genro-scriba — infrastructure configuration file generator (Traefik, Docker Compose, and more)

License

Apache License 2.0 — Copyright 2025 Softwell S.r.l.

Reusable named input constraints: see CustomTypes for ct.register(name, type, annotation), ct.name signatures, and Python/JS catalogs.

Metadata

Release files for genro-builders 0.29.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for genro-builders 0.29.0
File Size Uploaded
genro_builders-0.29.0.tar.gz 482.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for genro-builders 0.29.0
File Interpreter ABI Platform
genro_builders-0.29.0-py3-none-any.whl Python 3 none any Details

Total release size: 783.3 kB

Release files / genro_builders-0.29.0.tar.gz

Download URL genro_builders-0.29.0.tar.gz
Size 482.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e02f3fb90433b8a0070733bb7480e3a7e17b9532a1c7d06811aaf1d533737723
BLAKE2b-256 checksum
How to use checksums
b9c7334a5e783353c19fd2368bb6c3ba67cf7f5a52b1c4e07d6a3f1c52c41f51
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / genro_builders-0.29.0-py3-none-any.whl

Download URL genro_builders-0.29.0-py3-none-any.whl
Size 300.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a7bb4374a9d0785c23c969e92cc6dc43d34ec8f3a8877e18985707c1f41b569e
BLAKE2b-256 checksum
How to use checksums
e95b9e6d00b7fa43fb847a8d1a9dfb79d827082f0bf4bdf1c81305dc8160ab3b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.29.0 This release

2 release files

0.28.1

2 release files

0.28.0

2 release files

0.27.0

2 release files

0.26.0

2 release files

0.25.0

2 release files

0.24.0

2 release files

0.23.3

2 release files

0.23.1

2 release files

0.23.0

2 release files

0.22.0

2 release files

0.21.1

2 release files

0.21.0

2 release files

0.20.0

2 release files

0.16.0

2 release files

0.9.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page