Builders
Domain-specific fluent APIs: HTML, Markdown, XML Schema, custom DSLs.
When Do You Need Builders?
Structure has rules: HTML, XML, configuration schemas
Validation at build time: Catch errors early, not at runtime
Domain vocabulary: Methods like
div(),p()instead ofset_item()Output generation: Compile to HTML, Markdown, XML
Quick Start
from genro_builders import BuilderBag
from genro_builders.contrib.html import HtmlBuilder
html = BuilderBag(builder=HtmlBuilder)
div = html.div(id='main')
div.p(value='Hello World')
print(html.to_xml(pretty=True))
# <div id="main">
# <p>Hello World</p>
# </div>
The Key Insight
Without a builder:
bag = Bag()
bag.set_item('div', Bag())
bag['div'].set_item('p', 'Hello') # No validation, no structure
With a builder:
html = BuilderBag(builder=HtmlBuilder)
div = html.div() # Returns Bag for children
div.p(value='Hello') # Validated, returns BagNode
div.invalid() # Error! 'invalid' not in HTML schema
Built-in Builders
Builder |
Purpose |
|---|---|
|
HTML5 with 112 tags |
|
Markdown generation |
|
From XML Schema files |
Return Types
Container elements (can have children) → return
BagLeaf elements (no children) → return
BagNode
div = html.div() # Bag - can add children
meta = html.meta() # BagNode - leaf element
Custom Builders
Three decorators for defining schema:
Decorator |
Purpose |
Body |
|---|---|---|
|
Simple elements |
Required empty ( |
|
Inheritance groups |
Optional ( |
|
Composite structures |
Required |
@element Example
from genro_builders.builders import BagBuilderBase, element
class MenuBuilder(BagBuilderBase):
@element(sub_tags='item,separator')
def menu(self): ...
@element()
def item(self, label='', action=''): ...
@element()
def separator(self): ...
menu = BuilderBag(builder=MenuBuilder)
m = menu.menu()
m.item(label='Open', action='open_file')
m.separator()
m.item(label='Exit', action='quit')
@component Example
Use @component for reusable composite structures:
from genro_builders.builders import BagBuilderBase, element, component
class PageBuilder(BagBuilderBase):
@element()
def input(self): ...
@element()
def button(self): ...
@component(sub_tags='') # Closed component
def login_form(self, component: Bag, **kwargs):
component.input(name='username')
component.input(name='password')
component.button('Login')
return component
page = BuilderBag(builder=PageBuilder)
page.login_form() # Creates complete form structure
Output Generation
HTML/XML
html.to_xml(pretty=True)
Markdown
doc = BuilderBag(builder=MarkdownBuilder)
doc.h1("Title")
doc.p("Content")
doc.builder.build()
doc.builder.render() # Returns markdown string
Documentation
Quick Start — Get started in 5 minutes
Custom Builders — Create your own
HTML Builder — Full HTML5 support
Markdown Builder — Document generation
XSD Builder — XML Schema support
Examples — Practical patterns
FAQ — Common questions