Builders Module

The builders package provides typed APIs for constructing TreeStore hierarchies.

Module Structure

        graph TB
    subgraph "genro_treestore.builders"
        BASE[base.py<br/>BuilderBase]
        DEC[decorators.py<br/>@element, @valid_children]
        HTML[html.py<br/>HtmlBuilder]

        subgraph "rnc/"
            RNC[RncBuilder]
            LAZY[LazyRncBuilder]
        end

        subgraph "xsd/"
            XSD[XsdBuilder]
        end

        BASE --> HTML
        BASE --> RNC
        BASE --> XSD
        DEC --> BASE
    end
    

BuilderBase

class genro_treestore.BuilderBase[source]

Bases: ABC

Abstract base class for TreeStore builders.

A builder provides domain-specific methods for creating nodes in a TreeStore. There are two ways to define elements:

  1. Using @element decorator on methods:

    @element(children=’item’) def menu(self, target, tag, **attr):

    return self.child(target, tag, **attr)

    @element(tags=’fridge, oven, sink’) def appliance(self, target, tag, **attr):

    return self.child(target, tag, value=’’, **attr)

  2. Using _schema dict for external/dynamic definitions:
    class HtmlBuilder(BuilderBase):
    _schema = {

    ‘div’: {‘children’: ‘=flow’}, ‘br’: {‘leaf’: True}, ‘td’: {

    ‘children’: ‘=flow’, ‘attrs’: {

    ‘colspan’: {‘type’: ‘int’, ‘min’: 1, ‘default’: 1}, ‘rowspan’: {‘type’: ‘int’, ‘min’: 0, ‘default’: 1}, ‘scope’: {‘type’: ‘enum’, ‘values’: [‘row’, ‘col’]},

    }

    },

    }

Schema keys:
  • children: str or set of allowed child tags (supports =ref)

  • leaf: True if element has no children (value=’’)

  • attrs: dict of attribute specs for validation
    • type: ‘int’, ‘string’, ‘uri’, ‘bool’, ‘enum’, ‘idrefs’

    • required: True/False (default: False)

    • min/max: numeric constraints for int type

    • default: default value

    • values: list of valid values for enum type

The lookup order is: decorated methods first, then _schema. Attribute validation is performed with pure Python (no dependencies).

Usage:
>>> store = TreeStore(builder=MyBuilder())
>>> store.fridge()  # calls appliance() with tag='fridge'
classmethod __init_subclass__(**kwargs)[source]

Build the _element_tags dict from @element decorated methods.

Parameters:

kwargs (Any)

Return type:

None

__getattr__(name)[source]

Look up tag in _element_tags or _schema and return handler.

Parameters:

name (str)

Return type:

Any

child(target, tag, label=None, value=None, _position=None, _builder=None, **attr)[source]

Create a child node in the target TreeStore.

Parameters:
  • target (TreeStore) – The TreeStore to add the child to.

  • tag (str) – The node’s type (stored in node.tag).

  • label (str | None (default: None)) – Explicit label. If None, auto-generated as tag_N.

  • value (Any (default: None)) – If provided, creates a leaf node; otherwise creates a branch.

  • _position (str | None (default: None)) – Position specifier (see TreeStore.set_item for syntax).

  • _builder (BuilderBase | None (default: None)) – Override builder for this branch and its descendants. If None, inherits from target.

  • **attr (Any) – Node attributes.

Return type:

TreeStore | TreeStoreNode

Returns:

TreeStore if branch (for adding children), TreeStoreNode if leaf.

Example

>>> builder.child(store, 'div', id='main')
>>> builder.child(store, 'meta', value='', charset='utf-8')  # void
>>> builder.child(store, 'svg', _builder=SvgBuilder())
check(store, parent_tag=None, path='')[source]

Check the TreeStore structure against this builder’s rules.

Checks structure rules defined via @element(children=…) decorator: - valid_children: which tags can be children of this tag - cardinality: per-tag min/max constraints using slice syntax

Parameters:
  • store (TreeStore) – The TreeStore to check.

  • parent_tag (str | None (default: None)) – The tag of the parent node (for context).

  • path (str (default: '')) – Current path in the tree (for error messages).

Return type:

list[str]

Returns:

List of error messages (empty if valid).

Decorators

@element

@genro_treestore.element(tags='', children='', validate=True)[source]

Decorator to define element tags and validation rules for a builder method.

The decorator registers the method as handler for the specified tags. If no tags are specified, the method name is used as the tag.

Attribute validation is automatically extracted from function signature type hints when validate=True (default).

Parameters:
  • tags (str | tuple[str, ...] (default: '')) – Tag names this method handles. Can be: - A comma-separated string: ‘fridge, oven, sink’ - A tuple of strings: (‘fridge’, ‘oven’, ‘sink’) If empty, the method name is used as the single tag.

  • children (str | tuple[str, ...] (default: '')) –

    Valid child tag specs for structure validation. Can be: - A comma-separated string: ‘tag1, tag2[:1], tag3[1:]’ - A tuple of strings: (‘tag1’, ‘tag2[:1]’, ‘tag3[1:]’)

    Each spec can be: - ‘tag’ - allowed, no cardinality constraint (0..∞) - ‘tag[n]’ - exactly n required - ‘tag[n:]’ - at least n required - ‘tag[:m]’ - at most m allowed - ‘tag[n:m]’ - between n and m (inclusive) Empty string or empty tuple means no children allowed (leaf node).

  • validate (bool (default: True)) – If True (default), extract attribute validation rules from function signature type hints. Set to False to disable validation.

Return type:

Callable

Example

>>> class MyBuilder(BuilderBase):
...     # Multiple tags pointing to same method
...     @element(tags='fridge, oven, sink')
...     def appliance(self, target, tag, **attr):
...         return self.child(target, tag, value='', **attr)
...
...     # Structure validation with children
...     @element(children='section, item[1:]')
...     def menu(self, target, tag, **attr):
...         return self.child(target, tag, **attr)
...
...     @element()  # No children allowed (leaf)
...     def item(self, target, tag, **attr):
...         return self.child(target, tag, value='', **attr)
...
...     # Attribute validation from signature type hints
...     @element()
...     def td(self, target, tag, colspan: int = 1,
...            scope: Literal['row', 'col'] | None = None, **attr):
...         return self.child(target, tag, colspan=colspan, scope=scope, **attr)

@valid_children

@genro_treestore.valid_children(tags='', children='', validate=True)

Decorator to define element tags and validation rules for a builder method.

The decorator registers the method as handler for the specified tags. If no tags are specified, the method name is used as the tag.

Attribute validation is automatically extracted from function signature type hints when validate=True (default).

Parameters:
  • tags (str | tuple[str, ...] (default: '')) – Tag names this method handles. Can be: - A comma-separated string: ‘fridge, oven, sink’ - A tuple of strings: (‘fridge’, ‘oven’, ‘sink’) If empty, the method name is used as the single tag.

  • children (str | tuple[str, ...] (default: '')) –

    Valid child tag specs for structure validation. Can be: - A comma-separated string: ‘tag1, tag2[:1], tag3[1:]’ - A tuple of strings: (‘tag1’, ‘tag2[:1]’, ‘tag3[1:]’)

    Each spec can be: - ‘tag’ - allowed, no cardinality constraint (0..∞) - ‘tag[n]’ - exactly n required - ‘tag[n:]’ - at least n required - ‘tag[:m]’ - at most m allowed - ‘tag[n:m]’ - between n and m (inclusive) Empty string or empty tuple means no children allowed (leaf node).

  • validate (bool (default: True)) – If True (default), extract attribute validation rules from function signature type hints. Set to False to disable validation.

Return type:

Callable

Example

>>> class MyBuilder(BuilderBase):
...     # Multiple tags pointing to same method
...     @element(tags='fridge, oven, sink')
...     def appliance(self, target, tag, **attr):
...         return self.child(target, tag, value='', **attr)
...
...     # Structure validation with children
...     @element(children='section, item[1:]')
...     def menu(self, target, tag, **attr):
...         return self.child(target, tag, **attr)
...
...     @element()  # No children allowed (leaf)
...     def item(self, target, tag, **attr):
...         return self.child(target, tag, value='', **attr)
...
...     # Attribute validation from signature type hints
...     @element()
...     def td(self, target, tag, colspan: int = 1,
...            scope: Literal['row', 'col'] | None = None, **attr):
...         return self.child(target, tag, colspan=colspan, scope=scope, **attr)

HtmlBuilder

class genro_treestore.HtmlBuilder[source]

Bases: BuilderBase

Builder for HTML elements.

Provides dynamic methods for all HTML tags via __getattr__. Void elements (meta, br, img, etc.) automatically use empty string value.

Usage:
>>> store = TreeStore(builder=HtmlBuilder())
>>> store.div(id='main').p(value='Hello')
>>> store.ul().li(value='Item 1')
Categories available as class attributes for reference:
  • VOID_ELEMENTS

  • FLOW_CONTENT

  • PHRASING_CONTENT

  • etc.

VOID_ELEMENTS = frozenset({'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr'})
METADATA_CONTENT = frozenset({'base', 'link', 'meta', 'noscript', 'script', 'style', 'template', 'title'})
FLOW_CONTENT = frozenset({'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'main', 'map', 'mark', 'math', 'menu', 'meter', 'nav', 'noscript', 'object', 'ol', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'span', 'strong', 'sub', 'sup', 'svg', 'table', 'template', 'textarea', 'time', 'u', 'ul', 'var', 'video', 'wbr'})
PHRASING_CONTENT = frozenset({'a', 'abbr', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'mark', 'math', 'meter', 'noscript', 'object', 'output', 'picture', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'select', 'slot', 'small', 'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var', 'video', 'wbr'})
HEADING_CONTENT = frozenset({'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hgroup'})
SECTIONING_CONTENT = frozenset({'article', 'aside', 'nav', 'section'})
EMBEDDED_CONTENT = frozenset({'audio', 'canvas', 'embed', 'iframe', 'img', 'math', 'object', 'picture', 'svg', 'video'})
INTERACTIVE_CONTENT = frozenset({'a', 'audio', 'button', 'details', 'embed', 'iframe', 'img', 'input', 'label', 'select', 'textarea', 'video'})
ELEMENT_CHILDREN = {'audio': {'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'main', 'map', 'mark', 'math', 'menu', 'meter', 'nav', 'noscript', 'object', 'ol', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'source', 'span', 'strong', 'sub', 'sup', 'svg', 'table', 'template', 'textarea', 'time', 'track', 'u', 'ul', 'var', 'video', 'wbr'}, 'body': frozenset({'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'main', 'map', 'mark', 'math', 'menu', 'meter', 'nav', 'noscript', 'object', 'ol', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'span', 'strong', 'sub', 'sup', 'svg', 'table', 'template', 'textarea', 'time', 'u', 'ul', 'var', 'video', 'wbr'}), 'colgroup': {'col'}, 'datalist': {'option'}, 'details': {'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'main', 'map', 'mark', 'math', 'menu', 'meter', 'nav', 'noscript', 'object', 'ol', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'span', 'strong', 'sub', 'summary', 'sup', 'svg', 'table', 'template', 'textarea', 'time', 'u', 'ul', 'var', 'video', 'wbr'}, 'dl': {'dd', 'div', 'dt'}, 'fieldset': {'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'main', 'map', 'mark', 'math', 'menu', 'meter', 'nav', 'noscript', 'object', 'ol', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'span', 'strong', 'sub', 'sup', 'svg', 'table', 'template', 'textarea', 'time', 'u', 'ul', 'var', 'video', 'wbr'}, 'figure': {'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'main', 'map', 'mark', 'math', 'menu', 'meter', 'nav', 'noscript', 'object', 'ol', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'span', 'strong', 'sub', 'sup', 'svg', 'table', 'template', 'textarea', 'time', 'u', 'ul', 'var', 'video', 'wbr'}, 'head': frozenset({'base', 'link', 'meta', 'noscript', 'script', 'style', 'template', 'title'}), 'html': {'body', 'head'}, 'map': {'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'main', 'map', 'mark', 'math', 'menu', 'meter', 'nav', 'noscript', 'object', 'ol', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'span', 'strong', 'sub', 'sup', 'svg', 'table', 'template', 'textarea', 'time', 'u', 'ul', 'var', 'video', 'wbr'}, 'menu': {'li'}, 'ol': {'li'}, 'optgroup': {'option'}, 'picture': {'img', 'source'}, 'ruby': {'a', 'abbr', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'mark', 'math', 'meter', 'noscript', 'object', 'output', 'picture', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'select', 'slot', 'small', 'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var', 'video', 'wbr'}, 'select': {'optgroup', 'option'}, 'table': {'caption', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'}, 'tbody': {'tr'}, 'tfoot': {'tr'}, 'thead': {'tr'}, 'tr': {'td', 'th'}, 'ul': {'li'}, 'video': {'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'br', 'button', 'canvas', 'cite', 'code', 'data', 'datalist', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'main', 'map', 'mark', 'math', 'menu', 'meter', 'nav', 'noscript', 'object', 'ol', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'source', 'span', 'strong', 'sub', 'sup', 'svg', 'table', 'template', 'textarea', 'time', 'track', 'u', 'ul', 'var', 'video', 'wbr'}}
ALL_TAGS = frozenset({'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'math', 'menu', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select', 'slot', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'svg', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr'})
__getattr__(name)[source]

Dynamic method for any HTML tag.

Parameters:

name (str) – Tag name (e.g., ‘div’, ‘span’, ‘meta’)

Return type:

Callable[..., TreeStore | TreeStoreNode]

Returns:

Callable that creates a child with that tag.

Raises:

AttributeError – If name is not a valid HTML tag.

HTML5 Content Model

        graph TB
    subgraph "Content Categories"
        FLOW[Flow Content<br/>div, p, section...]
        PHRASING[Phrasing Content<br/>span, a, em...]
        EMBEDDED[Embedded Content<br/>img, video, iframe...]
        INTERACTIVE[Interactive Content<br/>a, button, input...]
        METADATA[Metadata Content<br/>head, title, meta...]
    end

    FLOW --> PHRASING
    FLOW --> EMBEDDED
    FLOW --> INTERACTIVE
    

RncBuilder

Dynamic builder from RELAX NG Compact schemas.

class genro_treestore.builders.RncBuilder[source]

Bases: BuilderBase

Builder dynamically generated from RNC (RELAX NG Compact) schema.

This builder reads an RNC schema and creates methods for all defined elements. The schema’s content model is used for validation.

_schema

Dict mapping element names to their specs (children, leaf, etc.)

_elements

Set of all valid element names from the schema

_void_elements

Set of elements that are void (no children, e.g., <br>)

_definitions

Raw definitions from the parsed RNC TreeStore

Parameters:
__init__(schema_store=None, void_elements=None)[source]

Initialize builder from a parsed RNC schema TreeStore.

Parameters:
  • schema_store (TreeStore | None (default: None)) – TreeStore from parse_rnc() or parse_rnc_file().

  • void_elements (set[str] | None (default: None)) – Optional set of element names that are void (self-closing, no content). If None, detected from schema.

classmethod from_rnc(content, void_elements=None)[source]

Create builder from RNC content string.

Parameters:
  • content (str) – RNC schema as string.

  • void_elements (set[str] | None (default: None)) – Optional set of void element names.

Return type:

RncBuilder

Returns:

RncBuilder instance.

Example

>>> builder = RncBuilder.from_rnc('''
...     start = html
...     html = element html { head, body }
...     head = element head { title }
...     title = element title { text }
...     body = element body { div* }
...     div = element div { text }
... ''')
classmethod from_rnc_file(filepath, void_elements=None)[source]

Create builder from RNC file.

Parameters:
  • filepath (str | Path) – Path to .rnc file.

  • void_elements (set[str] | None (default: None)) – Optional set of void element names.

Return type:

RncBuilder

Returns:

RncBuilder instance.

Example

>>> builder = RncBuilder.from_rnc_file('html5.rnc')
classmethod from_resolver(resolver, void_elements=None)[source]

Create builder from RNC directory resolver with lazy loading.

Loads schema files on demand as elements are used. This is useful for large schemas like HTML5 that are split across multiple files.

Parameters:
  • resolver (Any) – RncDirectoryResolver pointing to schema directory.

  • void_elements (set[str] | None (default: None)) – Optional set of void element names.

Return type:

RncBuilder

Returns:

RncBuilder instance with lazy loading support.

Example

>>> from genro_treestore.resolvers import html5_schema_resolver
>>> builder = RncBuilder.from_resolver(html5_schema_resolver())
classmethod html5(void_elements=None)[source]

Create builder for HTML5 schema from W3C validator.

Convenience method that creates a builder with the HTML5 schema from the W3C validator project, loaded lazily.

Parameters:

void_elements (set[str] | None (default: None)) – Optional override for void elements. If None, uses standard HTML5 void elements.

Return type:

RncBuilder

Returns:

RncBuilder for HTML5.

Example

>>> builder = RncBuilder.html5()
>>> store = TreeStore(builder=builder)
>>> store.html().head().title(value='My Page')
__getattr__(name)[source]

Dynamic method for any element in the schema.

Parameters:

name (str) – Element name.

Return type:

Callable[..., TreeStore | TreeStoreNode]

Returns:

Callable that creates a child with that element type.

Raises:

AttributeError – If name is not a valid element in the schema.

property elements: frozenset[str]

Return all valid element names in the schema.

property void_elements: frozenset[str]

Return void element names.

get_children(element)[source]

Get allowed children for an element.

Parameters:

element (str) – Element name.

Return type:

frozenset[str] | None

Returns:

Frozenset of allowed child element names, or None if any allowed.

Example

from genro_treestore import TreeStore
from genro_treestore.builders import RncBuilder

builder = RncBuilder.from_rnc('''
    start = document
    document = element doc { section+ }
    section = element section { title, para* }
    title = element title { text }
    para = element para { text }
''')

store = TreeStore(builder=builder)
doc = store.doc()
sec = doc.section()
sec.title(value='Introduction')
sec.para(value='Welcome.')

XsdBuilder

Dynamic builder from XML Schema (XSD) files.

class genro_treestore.XsdBuilder[source]

Bases: BuilderBase

Builder dynamically generated from XSD schema.

Creates methods for all elements defined in an XSD schema. Pass the schema TreeStore (from TreeStore.from_xml()) to __init__.

Example

>>> xsd = open('fatturapa.xsd').read()
>>> schema = TreeStore.from_xml(xsd)
>>> builder = XsdBuilder(schema)
>>>
>>> fattura = TreeStore(builder=builder)
>>> fattura.FatturaElettronica(versione='FPR12')
Parameters:

schema_store (TreeStore)

__init__(schema_store)[source]

Initialize builder from XSD schema TreeStore.

Parameters:

schema_store (TreeStore) – TreeStore from TreeStore.from_xml(xsd_string).

__getattr__(name)[source]

Dynamic method for any element in the schema.

Parameters:

name (str)

Return type:

Callable[..., TreeStore | TreeStoreNode]

property elements: frozenset[str]

Return all valid element names in the schema.

get_children(element)[source]

Get allowed children for an element.

Parameters:

element (str)

Return type:

frozenset[str] | None

Example

from genro_treestore import TreeStore
from genro_treestore.builders import XsdBuilder

# Load XSD schema
xsd_content = open('invoice.xsd').read()
schema = TreeStore.from_xml(xsd_content)
builder = XsdBuilder(schema)

# Build validated structure
store = TreeStore(builder=builder)
invoice = store.Invoice()
invoice.Header().Date(value='2025-01-01')

Builder Architecture

        classDiagram
    class BuilderBase {
        <<abstract>>
        +_schema: dict
        +child(tag, **attr)
        +__getattr__(name)
    }

    class HtmlBuilder {
        +div(**attr)
        +span(**attr)
        +p(**attr)
        ...
    }

    class RncBuilder {
        +from_rnc(content)
        +from_rnc_file(path)
    }

    class XsdBuilder {
        +__init__(schema)
    }

    BuilderBase <|-- HtmlBuilder
    BuilderBase <|-- RncBuilder
    BuilderBase <|-- XsdBuilder
    

Validation Flow

        flowchart TD
    ADD[Add child node]
    CHK{Valid child?}
    CARD{Cardinality OK?}
    OK[Node added]
    ERR[InvalidChildError]
    CERR[TooManyChildrenError]

    ADD --> CHK
    CHK -->|Yes| CARD
    CHK -->|No| ERR
    CARD -->|Yes| OK
    CARD -->|No| CERR
    

Cardinality Syntax

Syntax

Meaning

Example

tag

Zero or more

'div'

tag[:]

Zero or more

'div[:]'

tag[1]

Exactly one

'title[1]'

tag[0:1]

Zero or one

'subtitle[0:1]'

tag[1:]

One or more

'section[1:]'

tag[2:5]

Range

'item[2:5]'

See Also