Typed Builders
BuilderBase provides an abstract base class for creating domain-specific builders with a fluent API.
Why Use Builders?
Instead of using generic set_item() calls, builders let you:
Create type-safe methods for specific node types
Add validation for child relationships
Provide IDE autocompletion and type hints
Build domain-specific languages (DSLs)
Basic Builder
from genro_treestore import TreeStore
from genro_treestore.builders import BuilderBase, element
class HtmlBuilder(BuilderBase):
@element()
def div(self, target, tag, **attr):
"""Create a div element."""
return self.child(target, tag, **attr)
@element()
def span(self, target, tag, value=None, **attr):
"""Create a span element with optional text."""
return self.child(target, tag, value=value, **attr)
@element()
def ul(self, target, tag, **attr):
"""Create an unordered list."""
return self.child(target, tag, **attr)
@element()
def li(self, target, tag, value=None, **attr):
"""Create a list item."""
return self.child(target, tag, value=value, **attr)
Using the Builder
store = TreeStore(builder=HtmlBuilder())
# Build structure
main = store.div(id='main')
header = main.div(class_='header')
header.span(value='Welcome!')
nav = main.ul(class_='nav')
nav.li(value='Home')
nav.li(value='About')
nav.li(value='Contact')
# Access values
store['div_0.div_0.span_0'] # 'Welcome!'
store['div_0?id'] # 'main'
Method Chaining
Builder methods return new TreeStore/TreeStoreNode instances, enabling fluent chaining:
store = TreeStore(builder=HtmlBuilder())
(store
.div(id='container')
.div(class_='content')
.span(value='Nested content'))
Custom Constructors
Add specialized methods for common patterns:
class HtmlBuilder(BuilderBase):
@element()
def div(self, target, tag, **attr):
return self.child(target, tag, **attr)
@element()
def a(self, target, tag, href='#', value=None, **attr):
"""Create an anchor element."""
return self.child(target, tag, href=href, value=value, **attr)
@element()
def img(self, target, tag, src='', alt='', **attr):
"""Create an image element."""
return self.child(target, tag, src=src, alt=alt, value='', **attr)
Child Validation
Use the children parameter to specify which child tags are allowed:
class HtmlBuilder(BuilderBase):
@element(children='li') # Only 'li' children allowed
def ul(self, target, tag, **attr):
return self.child(target, tag, **attr)
@element() # Leaf element (no children)
def li(self, target, tag, value=None, **attr):
return self.child(target, tag, value=value, **attr)
@element()
def span(self, target, tag, value=None, **attr):
return self.child(target, tag, value=value, **attr)
# Usage
store = TreeStore(builder=HtmlBuilder())
ul = store.ul()
ul.li(value='Item 1') # OK
ul.li(value='Item 2') # OK
ul.span(value='Bad!') # Raises InvalidChildError
Inheritance
Extend builders for specialized domains:
class BaseBuilder(BuilderBase):
@element()
def container(self, target, tag, **attr):
return self.child(target, tag, **attr)
class FormBuilder(BaseBuilder):
@element()
def input(self, target, tag, name='', type_='text', **attr):
return self.child(target, tag, name=name, type=type_, value='', **attr)
@element()
def button(self, target, tag, value=None, **attr):
return self.child(target, tag, value=value, **attr)
@element(children='input, button')
def form(self, target, tag, action='', method='POST', **attr):
return self.child(target, tag, action=action, method=method, **attr)
Type Hints
Add type hints for better IDE support:
from typing import Self
class HtmlBuilder(BuilderBase):
@element()
def div(self, target, tag, **attr) -> TreeStore:
"""Create a div element."""
return self.child(target, tag, **attr)
@element()
def span(self, target, tag, value: str | None = None, **attr) -> TreeStoreNode:
"""Create a span element."""
return self.child(target, tag, value=value, **attr)
Complete Example
from genro_treestore import TreeStore
from genro_treestore.builders import BuilderBase, element
class MenuBuilder(BuilderBase):
"""Builder for navigation menus."""
@element(children='item, submenu')
def menu(self, target, tag, title: str = '', **attr):
"""Create a menu container."""
return self.child(target, tag, title=title, **attr)
@element(children='item, submenu')
def submenu(self, target, tag, title: str = '', **attr):
"""Create a submenu."""
return self.child(target, tag, title=title, **attr)
@element() # Leaf - no children
def item(self, target, tag, label: str = '', href: str = '#', **attr):
"""Create a menu item."""
return self.child(target, tag, label=label, href=href, value='', **attr)
# Usage
store = TreeStore(builder=MenuBuilder())
nav = store.menu(title='Main Navigation')
nav.item(label='Home', href='/')
nav.item(label='Products', href='/products')
more = nav.submenu(title='More')
more.item(label='About', href='/about')
more.item(label='Contact', href='/contact')
Next Steps
Learn about Validation with cardinality constraints