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:
ABCAbstract base class for TreeStore builders.
A builder provides domain-specific methods for creating nodes in a TreeStore. There are two ways to define elements:
- 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.
- 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:
- 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())
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:
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:
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:
BuilderBaseBuilder 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:
- 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:
BuilderBaseBuilder 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.
- classmethod from_rnc(content, void_elements=None)[source]
Create builder from RNC content string.
- Parameters:
- Return type:
- 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:
- Return type:
- 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:
- Return type:
- 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:
- 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:
- Returns:
Callable that creates a child with that element type.
- Raises:
AttributeError – If name is not a valid element in the schema.
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:
BuilderBaseBuilder 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).
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 |
|---|---|---|
|
Zero or more |
|
|
Zero or more |
|
|
Exactly one |
|
|
Zero or one |
|
|
One or more |
|
|
Range |
|
See Also
Builders Guide - Creating custom builders
Validation Guide - Validation rules