Store Module

TreeStore package - Hierarchical data container with O(1) lookup.

This package provides the core TreeStore data structure, a hierarchical container optimized for path-based access with constant-time lookup.

Architecture:

TreeStore uses a dual-reference pattern where:

  • TreeStore is a container of TreeStoreNode children

  • TreeStoreNode wraps a value (leaf) or another TreeStore (branch)

  • Each node maintains a reference to its parent TreeStore

  • Each TreeStore maintains a reference to its parent node (if any)

This enables bidirectional navigation and efficient path resolution.

Modules:
  • core: TreeStore class with path traversal, CRUD operations, iteration, serialization (TYTX, XML), and builder integration

  • node: TreeStoreNode class representing individual tree nodes with attributes, values, resolvers, and subscription support

  • loading: Functions for populating TreeStore from various sources (dict, list, another TreeStore)

  • subscription: Reactive event system for change notifications with hierarchical propagation

  • serialization: TYTX format serialization for type-preserving round-trip storage

Path Syntax:

TreeStore supports a rich path syntax for navigation:

store['config.database.host']     # Dotted path
store['users.#0.name']            # Positional index
store['users.#-1']                # Negative index (last)
store['config?timeout']           # Attribute access

Example

Basic usage with nested data:

from genro_treestore import TreeStore

store = TreeStore()
store.set_item('app.name', 'MyApp')
store.set_item('app.version', '1.0.0')
store.set_item('app.settings.debug', True)

# Path access
print(store['app.name'])              # 'MyApp'
print(store['app.settings.debug'])    # True

# Iteration
for key, value in store['app'].items():
    print(f"{key}: {value}")

With builder pattern:

from genro_treestore import TreeStore
from genro_treestore.builders import HtmlBuilder

store = TreeStore(builder=HtmlBuilder())
body = store.body()
body.div(id='main').p(value='Hello!')

See also

class genro_treestore.store.TreeStore[source]

Bases: SubscriptionMixin

A hierarchical data container with O(1) lookup.

TreeStore provides: - set_item(path, value, **attr): Create/update nodes with autocreate - get_item(path) / store[path]: Get values - get_attr(path, attr) / set_attr(path, **attr): Attribute access - digest(what): Extract data with #k, #v, #a syntax

The internal storage uses dict for O(1) lookup performance.

parent

The TreeStoreNode that contains this store as its value, or None if this is a root store.

Example

>>> store = TreeStore()
>>> store.set_item('html.body.div', color='red')
>>> store['html.body.div?color']
'red'
Parameters:
parent: TreeStoreNode | None
__contains__(label)[source]

Check if a label exists at root level or as a path.

Parameters:

label (str) – Label or dotted path to check.

Return type:

bool

Returns:

True if the label/path exists, False otherwise.

__getattr__(name)[source]

Delegate attribute access to builder if present.

If a builder is set and has a method matching the name, returns a callable that invokes the builder method with this store as the target.

Parameters:

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

Return type:

Any

Returns:

Callable that creates a child via the builder.

Raises:

AttributeError – If no builder or builder has no such method.

__getitem__(path)[source]

Get value or attribute by path.

Parameters:

path (str) – Dotted path, with optional ?attr or positional #N syntax.

Return type:

Any

Returns:

Value at path, or attribute if ?attr used.

Raises:

KeyError – If path not found.

Example

>>> store['html.body.div']  # value
>>> store['html.body.div?color']  # attribute
>>> store['#0.#1']  # positional access
__init__(source=None, parent=None, builder=None, raise_on_error=True)[source]

Initialize a TreeStore.

Parameters:
  • source (dict | list | TreeStore | None (default: None)) –

    Optional initial data. Can be: - dict: Nested dict converted to nodes. Keys with ‘_’ prefix

    are treated as attributes (e.g., {‘_color’: ‘red’, ‘child’: …})

    • TreeStore: Copy from another TreeStore

    • list: List of tuples (label, value) or (label, value, attr)

  • parent (TreeStoreNode | None (default: None)) – The TreeStoreNode that contains this store as its value.

  • builder (Any | None (default: None)) – Optional builder object that provides domain-specific methods. When set, attribute access delegates to the builder, enabling fluent API like store.div(), store.meta(), etc.

  • raise_on_error (bool (default: True)) – If True (default), raises ValueError on hard errors (invalid attributes, invalid child tags, too many children). Soft errors (missing required children) are always collected in node._invalid_reasons without raising. If False, all errors are collected without raising.

Return type:

None

Example

>>> TreeStore({'a': 1, 'b': {'c': 2}})
>>> TreeStore([('x', 1), ('y', 2, {'color': 'red'})])
>>> TreeStore(other_store)  # copy
>>> TreeStore(builder=HtmlBodyBuilder())  # with builder
>>> TreeStore(builder=HtmlBuilder(), raise_on_error=False)  # permissive mode
__iter__()[source]

Iterate over direct child nodes in insertion order.

Return type:

Iterator[TreeStoreNode]

__len__()[source]

Return the number of direct children in this store.

Return type:

int

__repr__()[source]

Return string representation showing node labels.

Return type:

str

__setitem__(path, value)[source]

Set value or attribute by path.

Parameters:
  • path (str) – Dotted path. Use ?attr suffix to set attribute.

  • value (Any) – Value to set.

Return type:

None

Example

>>> store['html.body.div'] = 'text'  # set value
>>> store['html.body.div?color'] = 'red'  # set attribute
as_dict()[source]

Convert to plain dict (recursive).

Branch nodes become nested dicts with their attributes and children. Leaf nodes become their value directly (or dict with _value if has attrs).

Return type:

dict[str, Any]

Returns:

Nested dictionary representation of the tree.

property builder: Any

Access the builder instance.

clear()[source]

Remove all nodes from this store.

Does not trigger deletion events for individual nodes.

Return type:

None

del_item(path)[source]

Delete and return node at path.

Parameters:

path (str) – Path to the node.

Return type:

TreeStoreNode

Returns:

The removed TreeStoreNode.

Raises:

KeyError – If path not found.

property depth: int

Get the depth of this store in the hierarchy (root=0).

digest(what='#k,#v')[source]

Extract data from nodes using digest syntax.

Parameters:

what (str (default: '#k,#v')) – Comma-separated specifiers: - #k: labels - #v: values - #a: all attributes (dict) - #a.attrname: specific attribute

Return type:

list[Any]

Returns:

List of values, or list of tuples if multiple specifiers.

Example

>>> store.digest('#k')  # ['label1', 'label2']
>>> store.digest('#v')  # [value1, value2]
>>> store.digest('#k,#v')  # [('label1', val1), ('label2', val2)]
>>> store.digest('#a.color')  # ['red', 'blue']
flattened(path_registry=None)[source]

Yield flat tuples representing the tree in depth-first order.

Converts the hierarchical tree structure into a flat sequence of tuples, suitable for serialization. Each tuple contains all information needed to reconstruct a node: its parent reference, label, tag, value, and attributes.

This method is the foundation for TYTX serialization (see to_tytx()).

Output Modes:
Normal mode (path_registry=None):

Parent references are full path strings. More readable and compresses very well with gzip due to repetitive patterns.

Compact mode (path_registry=dict):

Parent references are sequential numeric codes (0, 1, 2…). The provided dict is populated with {code: path} mappings. Produces smaller output without compression (~32% smaller), but gzip actually compresses normal mode better.

Parameters:

path_registry (dict[int, str] | None (default: None)) –

Optional dict to enable compact mode. - If None: parent is emitted as path string (normal mode) - If dict: parent is emitted as numeric code, and the dict

is populated with {code: full_path} mappings for branches

Yields:

tuple

(parent, label, tag, value, attr) where:
  • parent: Reference to parent node
    • Normal mode: path string (’’ for root-level nodes)

    • Compact mode: int code (None for root-level nodes)

  • label: Node’s unique key within its parent (e.g., ‘floor_0’)

  • tag: Node type from builder (e.g., ‘floor’) or None

  • value: Node value
    • None for branch nodes (nodes with children)

    • Actual value for leaf nodes (scalar values)

  • attr: Dict of node attributes (always a copy)

Return type:

Iterator[tuple[str | int | None, str, str | None, Any, dict[str, Any]]]

Note

  • Iteration order is depth-first (parent before children)

  • Branch nodes have value=None because their “value” is the child store

  • The path_registry dict is modified in place during iteration

  • Uses walk() internally for tree traversal

Example

Normal mode (path strings):

>>> store = TreeStore(builder=BuildingBuilder())
>>> b = store.building(name='Casa Mia')
>>> b.floor(number=1).room(name='Kitchen')
>>>
>>> for row in store.flattened():
...     parent, label, tag, value, attr = row
...     print(f"{parent!r:20} {label:15} {tag}")
''                   building_0      building
'building_0'         floor_0         floor
'building_0.floor_0' room_0          room

Compact mode (numeric codes):

>>> paths = {}
>>> for row in store.flattened(path_registry=paths):
...     parent, label, tag, value, attr = row
...     print(f"{parent!r:5} {label:15} {tag}")
None  building_0      building
0     floor_0         floor
1     room_0          room
>>>
>>> paths
{0: 'building_0', 1: 'building_0.floor_0'}

See also

  • to_tytx(): Serializes using this method

  • walk(): The underlying tree traversal method

classmethod from_tytx(data, transport=None, builder=None)[source]

Deserialize TreeStore from TYTX format with type preservation.

Reconstructs a TreeStore from TYTX-serialized data. Automatically detects normal vs compact format. Types (Decimal, date, datetime, time) are preserved exactly as they were before serialization.

Parameters:
  • data (str | bytes) – Serialized data from to_tytx(). - str: If transport is None or ‘json’ - bytes: If transport is ‘msgpack’

  • transport (Optional[Literal['json', 'msgpack']] (default: None)) – Input format (must match serialization): - None or ‘json’: Parse as JSON string (default) - ‘msgpack’: Parse as MessagePack bytes

  • builder (Any | None (default: None)) – Optional builder for the reconstructed store. Enables builder methods (e.g., store.div()) on the result.

Returns:

Fully reconstructed tree with:
  • Complete node hierarchy

  • All values with original types preserved

  • All node attributes restored

Return type:

TreeStore

Raises:

ImportError – If genro-tytx package is not installed.

Example

>>> # Basic round-trip
>>> original = TreeStore()
>>> original.set_item('config.price', Decimal('99.99'))
>>> data = original.to_tytx()
>>>
>>> restored = TreeStore.from_tytx(data)
>>> restored['config.price']  # Decimal('99.99'), not float
>>>
>>> # With MessagePack
>>> data = original.to_tytx(transport='msgpack')
>>> restored = TreeStore.from_tytx(data, transport='msgpack')
>>>
>>> # With builder
>>> restored = TreeStore.from_tytx(data, builder=HtmlBuilder())

See also

  • to_tytx(): Serialize TreeStore to TYTX format

classmethod from_xml(data, builder=None)[source]

Load TreeStore from XML string.

Each XML element becomes a node with:

  • label: tag name with counter suffix (element_0, element_1, etc.)

  • value: text content (leaf) or child TreeStore (branch)

  • attr: XML attributes, plus ‘_tag’ with namespace prefix if present

Namespace Handling:

XML namespaces are processed as follows:

  • Namespace declarations (xmlns:prefix=”uri”) are extracted

  • Element tags with namespaces store the prefixed form in ‘_tag’

  • The label uses only the local name (without namespace)

  • Namespaced attributes are filtered out (not preserved)

Example with namespaces:

xml = '''<root xmlns:ns="http://example.com">
    <ns:item>value</ns:item>
</root>'''
store = TreeStore.from_xml(xml)
# Label: 'item_0', attr['_tag']: 'ns:item'
Parameters:
  • data (str) – XML string to parse.

  • builder (Any | None (default: None)) – Optional builder for the resulting store.

Return type:

TreeStore

Returns:

TreeStore with XML structure. The root element becomes the first (and typically only) top-level node.

Example

Simple XML:

>>> xml = '<html><head><title>Hello</title></head></html>'
>>> store = TreeStore.from_xml(xml)
>>> store['html_0.head_0.title_0']
'Hello'

Accessing tag and attributes:

>>> xml = '<div class="main"><span id="x">text</span></div>'
>>> store = TreeStore.from_xml(xml)
>>> node = store.get_node('div_0.span_0')
>>> node.attr['id']
'x'
>>> node.value
'text'

See also

  • to_xml() - Convert TreeStore back to XML

get(label, default=None)[source]

Get node by label at this level, with default.

Unlike get_node(), this only looks at direct children (no path traversal).

Parameters:
  • label (str) – Node label to find.

  • default (Any (default: None)) – Value to return if not found.

Return type:

TreeStoreNode | None

Returns:

TreeStoreNode if found, default otherwise.

get_attr(path, attr=None, default=None)[source]

Get attribute(s) from node at path.

Parameters:
  • path (str) – Path to the node.

  • attr (str | None (default: None)) – Attribute name. If None, returns all attributes.

  • default (Any (default: None)) – Default value if attribute not found.

Return type:

Any

Returns:

Attribute value, all attributes dict, or default.

get_item(path, default=None)[source]

Get the value at the given path.

Parameters:
  • path (str) – Dotted path, optionally with ?attr suffix.

  • default (Any (default: None)) – Default value if path not found.

Return type:

Any

Returns:

The value at the path, attribute value, or default.

Example

>>> store.get_item('html.body.div')  # returns value
>>> store.get_item('html.body.div?color')  # returns attribute
get_node(path)[source]

Get node at the given path.

Parameters:

path (str) – Dotted path to the node.

Return type:

TreeStoreNode | None

Returns:

TreeStoreNode at the path, or None if not found.

get_nodes(path='')[source]

Get nodes at path (or root if empty).

Parameters:

path (str (default: '')) – Optional path to get nodes from.

Return type:

list[TreeStoreNode]

Returns:

List of TreeStoreNode at the specified level in insertion order.

get_resolver(path)[source]

Get the resolver from the node at the given path.

Parameters:

path (str) – Path to the node.

Return type:

Any

Returns:

The resolver, or None if no resolver is set.

property is_valid: bool

True if all nodes in this store are valid.

Recursively checks all nodes in the tree for validation errors.

Returns:

True if no node has validation errors, False otherwise.

Example

>>> store = TreeStore(builder=HtmlBuilder())
>>> thead = store.thead()
>>> store.is_valid
False  # thead requires at least 1 tr
>>> thead.tr()
>>> store.is_valid
True
items()[source]

Return list of (label, value) pairs in insertion order.

Return type:

list[tuple[str, Any]]

iter_digest(what='#k,#v')[source]

Yield data from nodes using digest syntax.

Parameters:

what (str (default: '#k,#v')) – Comma-separated specifiers: - #k: labels - #v: values - #a: all attributes (dict) - #a.attrname: specific attribute

Yields:

Values, or tuples if multiple specifiers.

Return type:

Iterator[Any]

Example

>>> for label in store.iter_digest('#k'):
...     print(label)
iter_items()[source]

Yield (label, value) pairs in insertion order.

Return type:

Iterator[tuple[str, Any]]

iter_keys()[source]

Yield labels at this level in insertion order.

Return type:

Iterator[str]

iter_nodes()[source]

Yield nodes at this level in insertion order.

Return type:

Iterator[TreeStoreNode]

iter_values()[source]

Yield values at this level in insertion order.

Return type:

Iterator[Any]

keys()[source]

Return list of labels at this level in insertion order.

Return type:

list[str]

nodes()[source]

Return list of nodes at this level in insertion order.

Return type:

list[TreeStoreNode]

property parent_node: TreeStoreNode | None

Get the parent node (alias for self.parent).

pop(path, default=None)[source]

Remove and return value at path.

Parameters:
  • path (str) – Path to the node.

  • default (Any (default: None)) – Default value if path not found.

Return type:

Any

Returns:

The value of the removed node, or default.

property root: TreeStore

Get the root TreeStore of this hierarchy.

set_attr(path, _attributes=None, **kwargs)[source]

Set attributes on node at path.

Parameters:
  • path (str) – Path to the node.

  • _attributes (dict[str, Any] | None (default: None)) – Dictionary of attributes.

  • **kwargs (Any) – Additional attributes as keyword arguments.

Return type:

None

set_item(path, value=None, _attributes=None, _position=None, **kwargs)[source]

Set an item at the given path, creating intermediate nodes as needed.

Parameters:
  • path (str) – Dotted path to the item (e.g., ‘html.body.div’).

  • value (Any (default: None)) – The value to store. If None, creates a branch node.

  • _attributes (dict[str, Any] | None (default: None)) – Dictionary of attributes.

  • _position (str | None (default: None)) – Position specifier: - None or ‘>’: append to end (default) - ‘<’: insert at beginning - ‘<label’: insert before label - ‘>label’: insert after label - ‘<#N’: insert before position N - ‘>#N’: insert after position N - ‘#N’: insert at exact position N

  • **kwargs (Any) – Additional attributes as keyword arguments.

Returns:

  • If branch created: returns the new branch’s TreeStore

  • If leaf created: returns the parent TreeStore

Return type:

TreeStore for fluent chaining

Example

>>> store.set_item('html').set_item('body').set_item('div', color='red')
>>> store.set_item('ul').set_item('li', 'Item 1').set_item('li', 'Item 2')
>>> store.set_item('first', 'value', _position='<')  # insert at beginning
set_resolver(path, resolver)[source]

Set a resolver on the node at the given path.

Parameters:
  • path (str) – Path to the node.

  • resolver (Any) – The resolver to set.

Return type:

None

to_tytx(transport=None, compact=False)[source]

Serialize TreeStore to TYTX format with type preservation.

TYTX (Typed Transport) preserves Python types (Decimal, date, datetime, time) in the serialized format, eliminating manual type conversion when deserializing.

The tree is serialized as a flat list of row tuples in depth-first order. Each row contains: (parent, label, tag, value, attr).

Parameters:
  • transport (Optional[Literal['json', 'msgpack']] (default: None)) –

    Output format: - None or ‘json’: JSON string (default). Human-readable,

    compresses very well with gzip.

    • ’msgpack’: Binary MessagePack bytes. ~30% smaller than JSON before compression.

  • compact (bool (default: False)) –

    Serialization mode: - False (default): Parent as path strings (‘a.b.c’).

    Recommended with gzip compression.

    • True: Parent as numeric codes (0, 1, 2…). ~32% smaller without compression, but gzip prefers normal.

Returns:

If transport is None or ‘json’ bytes: If transport is ‘msgpack’

Return type:

str

Raises:

ImportError – If genro-tytx package is not installed.

Example

>>> from decimal import Decimal
>>> from datetime import date
>>>
>>> store = TreeStore()
>>> store.set_item('invoice.amount', Decimal('1234.56'))
>>> store.set_item('invoice.date', date(2025, 1, 15))
>>>
>>> # Default JSON format
>>> json_data = store.to_tytx()
>>>
>>> # Binary MessagePack (smaller)
>>> msgpack_data = store.to_tytx(transport='msgpack')
>>>
>>> # Compact mode (for uncompressed transmission)
>>> compact_data = store.to_tytx(compact=True)

See also

  • from_tytx(): Deserialize back to TreeStore

  • flattened(): The underlying flat representation

to_xml(root_tag=None)[source]

Serialize TreeStore to XML string.

Converts the TreeStore hierarchy into an XML document. Each node becomes an XML element with:

  • tag: from node’s ‘_tag’ attribute, or label without suffix

  • attributes: node’s attr dict (excluding internal _* keys)

  • content: text value (if leaf) or child elements (if branch)

Root Element Handling:

The root element is determined as follows:

  • If root_tag is provided, it wraps all content

  • If store has exactly one top-level node and no root_tag, that node becomes the root element directly

  • If store has multiple top-level nodes and no root_tag, they are wrapped in a <root> element

Tag Resolution:

For each node, the XML tag is resolved in order:

  1. node.attr['_tag'] - Explicit tag (may include namespace prefix)

  2. node.label.rsplit('_', 1)[0] - Label without counter suffix

This allows round-trip preservation when loading from XML.

Parameters:

root_tag (str | None (default: None)) – Optional root element tag. If None and store has exactly one root node, uses that node’s tag. If None and store has multiple nodes, defaults to ‘root’.

Return type:

str

Returns:

XML string representation without XML declaration.

Example

Single root node:

>>> store = TreeStore()
>>> store.set_item('html.head.title', 'Hello')
>>> print(store.to_xml())
<html><head><title>Hello</title></head></html>

Multiple top-level nodes:

>>> store = TreeStore()
>>> store.set_item('item', 'first')
>>> store.set_item('item', 'second')
>>> print(store.to_xml())
<root><item>first</item><item>second</item></root>

With explicit root tag:

>>> store = TreeStore()
>>> store.set_item('item', 'value')
>>> print(store.to_xml(root_tag='items'))
<items><item>value</item></items>

See also

update(other, ignore_none=False)[source]

Update this TreeStore with data from another source.

For each item in other: - If label exists: updates attributes, and if both are branches,

recursively updates children; otherwise replaces value

  • If label doesn’t exist: adds the new node

Parameters:
  • other (dict | list | TreeStore) – Source data (dict, list of tuples, or TreeStore)

  • ignore_none (bool (default: False)) – If True, don’t update values that are None

Return type:

None

Example

>>> store = TreeStore({'config': {'a': 1, 'b': 2}})
>>> store.update({'config': {'b': 3, 'c': 4}})
>>> store['config.a']  # 1 (preserved)
>>> store['config.b']  # 3 (updated)
>>> store['config.c']  # 4 (added)
validation_errors()[source]

Return all validation errors in the tree.

Return type:

dict[str, list[str]]

Returns:

Dictionary mapping node paths to their error lists. Only includes nodes with errors.

Example

>>> store = TreeStore(builder=HtmlBuilder())
>>> thead = store.thead()
>>> store.validation_errors()
{'thead_0': ["requires at least 1 'tr', has 0"]}
values()[source]

Return list of values at this level in insertion order.

Return type:

list[Any]

walk(callback=None, _prefix='')[source]

Walk the tree, optionally calling a callback on each node.

Parameters:
  • callback (Optional[Callable[[TreeStoreNode], Any]] (default: None)) – Optional function to call on each node. If provided, walk returns None.

  • _prefix (str (default: '')) – Internal use for path building.

Yields:

Tuples of (path, node) if no callback provided.

Return type:

Optional[Iterator[tuple[str, TreeStoreNode]]]

Example

>>> for path, node in store.walk():
...     print(path, node.value)
>>> store.walk(lambda n: print(n.label))
class genro_treestore.store.TreeStoreNode[source]

Bases: object

A node in a TreeStore hierarchy.

Each node has: - label: The node’s unique name/key within its parent - attr: Dictionary of attributes - value: Either a scalar value or a TreeStore (for children) - parent: Reference to the containing TreeStore - tag: Optional type/tag for the node (used by builders)

Example

>>> node = TreeStoreNode('user', {'id': 1}, 'Alice')
>>> node.label
'user'
>>> node.value
'Alice'
Parameters:
label
attr
parent
tag
__init__(label, attr=None, value=None, parent=None, tag=None, resolver=None)[source]

Initialize a TreeStoreNode.

Parameters:
  • label (str) – The node’s unique name/key.

  • attr (dict[str, Any] | None (default: None)) – Optional dictionary of attributes.

  • value (Any (default: None)) – The node’s value (scalar or TreeStore for children).

  • parent (TreeStore | None (default: None)) – The TreeStore containing this node.

  • tag (str | None (default: None)) – Optional type/tag for the node (used by builders).

  • resolver (TreeStoreResolver | None (default: None)) – Optional resolver for lazy/dynamic value computation.

Return type:

None

get_attr(attr=None, default=None)[source]

Get attribute value or all attributes.

Parameters:
  • attr (str | None (default: None)) – Attribute name. If None, returns all attributes.

  • default (Any (default: None)) – Default value if attribute not found.

Return type:

Any

Returns:

Attribute value, default, or dict of all attributes.

property is_branch: bool

True if this node contains a TreeStore (has children).

property is_leaf: bool

True if this node contains a scalar value.

property is_valid: bool

True if this node has no validation errors.

property resolver: TreeStoreResolver | None

Get the node’s resolver.

set_attr(_attr=None, trigger=True, reason=None, **kwargs)[source]

Set attributes on the node.

Parameters:
  • _attr (dict[str, Any] | None (default: None)) – Dictionary of attributes to set.

  • trigger (bool (default: True)) – If True, notify subscribers of the change.

  • reason (str | None (default: None)) – Optional reason string for the trigger.

  • **kwargs (Any) – Additional attributes as keyword arguments.

Return type:

None

set_value(value, trigger=True, reason=None)[source]

Set the node’s value, optionally triggering events.

Parameters:
  • value (Any) – The new value.

  • trigger (bool (default: True)) – If True, notify subscribers of the change.

  • reason (str | None (default: None)) – Optional reason string for the trigger.

Return type:

None

subscribe(subscriber_id, callback)[source]

Subscribe to changes on this specific node.

Parameters:
  • subscriber_id (str) – Unique identifier for this subscription.

  • callback (Callable[..., None]) – Function to call on changes.

Return type:

None

Callback signature:

callback(node, info, evt) - node: This TreeStoreNode - info: oldvalue (for ‘upd_value’) or list of changed attrs (for ‘upd_attr’) - evt: Event type (‘upd_value’ or ‘upd_attr’)

Example

>>> def on_change(node, info, evt):
...     print(f"{evt}: {info}")
>>> node.subscribe('watcher', on_change)
unsubscribe(subscriber_id)[source]

Unsubscribe from changes on this node.

Parameters:

subscriber_id (str) – The subscription identifier to remove.

Return type:

None

property value: Any

Get the node’s value.

If a resolver is set, triggers resolution instead of returning the stored value.

Classes

TreeStore

class genro_treestore.TreeStore[source]

Bases: SubscriptionMixin

A hierarchical data container with O(1) lookup.

TreeStore provides: - set_item(path, value, **attr): Create/update nodes with autocreate - get_item(path) / store[path]: Get values - get_attr(path, attr) / set_attr(path, **attr): Attribute access - digest(what): Extract data with #k, #v, #a syntax

The internal storage uses dict for O(1) lookup performance.

parent

The TreeStoreNode that contains this store as its value, or None if this is a root store.

Example

>>> store = TreeStore()
>>> store.set_item('html.body.div', color='red')
>>> store['html.body.div?color']
'red'
Parameters:
__init__(source=None, parent=None, builder=None, raise_on_error=True)[source]

Initialize a TreeStore.

Parameters:
  • source (dict | list | TreeStore | None (default: None)) –

    Optional initial data. Can be: - dict: Nested dict converted to nodes. Keys with ‘_’ prefix

    are treated as attributes (e.g., {‘_color’: ‘red’, ‘child’: …})

    • TreeStore: Copy from another TreeStore

    • list: List of tuples (label, value) or (label, value, attr)

  • parent (TreeStoreNode | None (default: None)) – The TreeStoreNode that contains this store as its value.

  • builder (Any | None (default: None)) – Optional builder object that provides domain-specific methods. When set, attribute access delegates to the builder, enabling fluent API like store.div(), store.meta(), etc.

  • raise_on_error (bool (default: True)) – If True (default), raises ValueError on hard errors (invalid attributes, invalid child tags, too many children). Soft errors (missing required children) are always collected in node._invalid_reasons without raising. If False, all errors are collected without raising.

Return type:

None

Example

>>> TreeStore({'a': 1, 'b': {'c': 2}})
>>> TreeStore([('x', 1), ('y', 2, {'color': 'red'})])
>>> TreeStore(other_store)  # copy
>>> TreeStore(builder=HtmlBodyBuilder())  # with builder
>>> TreeStore(builder=HtmlBuilder(), raise_on_error=False)  # permissive mode
parent: TreeStoreNode | None
__repr__()[source]

Return string representation showing node labels.

Return type:

str

__len__()[source]

Return the number of direct children in this store.

Return type:

int

__iter__()[source]

Iterate over direct child nodes in insertion order.

Return type:

Iterator[TreeStoreNode]

__contains__(label)[source]

Check if a label exists at root level or as a path.

Parameters:

label (str) – Label or dotted path to check.

Return type:

bool

Returns:

True if the label/path exists, False otherwise.

__getattr__(name)[source]

Delegate attribute access to builder if present.

If a builder is set and has a method matching the name, returns a callable that invokes the builder method with this store as the target.

Parameters:

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

Return type:

Any

Returns:

Callable that creates a child via the builder.

Raises:

AttributeError – If no builder or builder has no such method.

property builder: Any

Access the builder instance.

set_item(path, value=None, _attributes=None, _position=None, **kwargs)[source]

Set an item at the given path, creating intermediate nodes as needed.

Parameters:
  • path (str) – Dotted path to the item (e.g., ‘html.body.div’).

  • value (Any (default: None)) – The value to store. If None, creates a branch node.

  • _attributes (dict[str, Any] | None (default: None)) – Dictionary of attributes.

  • _position (str | None (default: None)) – Position specifier: - None or ‘>’: append to end (default) - ‘<’: insert at beginning - ‘<label’: insert before label - ‘>label’: insert after label - ‘<#N’: insert before position N - ‘>#N’: insert after position N - ‘#N’: insert at exact position N

  • **kwargs (Any) – Additional attributes as keyword arguments.

Returns:

  • If branch created: returns the new branch’s TreeStore

  • If leaf created: returns the parent TreeStore

Return type:

TreeStore for fluent chaining

Example

>>> store.set_item('html').set_item('body').set_item('div', color='red')
>>> store.set_item('ul').set_item('li', 'Item 1').set_item('li', 'Item 2')
>>> store.set_item('first', 'value', _position='<')  # insert at beginning
get_item(path, default=None)[source]

Get the value at the given path.

Parameters:
  • path (str) – Dotted path, optionally with ?attr suffix.

  • default (Any (default: None)) – Default value if path not found.

Return type:

Any

Returns:

The value at the path, attribute value, or default.

Example

>>> store.get_item('html.body.div')  # returns value
>>> store.get_item('html.body.div?color')  # returns attribute
__getitem__(path)[source]

Get value or attribute by path.

Parameters:

path (str) – Dotted path, with optional ?attr or positional #N syntax.

Return type:

Any

Returns:

Value at path, or attribute if ?attr used.

Raises:

KeyError – If path not found.

Example

>>> store['html.body.div']  # value
>>> store['html.body.div?color']  # attribute
>>> store['#0.#1']  # positional access
__setitem__(path, value)[source]

Set value or attribute by path.

Parameters:
  • path (str) – Dotted path. Use ?attr suffix to set attribute.

  • value (Any) – Value to set.

Return type:

None

Example

>>> store['html.body.div'] = 'text'  # set value
>>> store['html.body.div?color'] = 'red'  # set attribute
get_node(path)[source]

Get node at the given path.

Parameters:

path (str) – Dotted path to the node.

Return type:

TreeStoreNode | None

Returns:

TreeStoreNode at the path, or None if not found.

get_attr(path, attr=None, default=None)[source]

Get attribute(s) from node at path.

Parameters:
  • path (str) – Path to the node.

  • attr (str | None (default: None)) – Attribute name. If None, returns all attributes.

  • default (Any (default: None)) – Default value if attribute not found.

Return type:

Any

Returns:

Attribute value, all attributes dict, or default.

set_attr(path, _attributes=None, **kwargs)[source]

Set attributes on node at path.

Parameters:
  • path (str) – Path to the node.

  • _attributes (dict[str, Any] | None (default: None)) – Dictionary of attributes.

  • **kwargs (Any) – Additional attributes as keyword arguments.

Return type:

None

set_resolver(path, resolver)[source]

Set a resolver on the node at the given path.

Parameters:
  • path (str) – Path to the node.

  • resolver (Any) – The resolver to set.

Return type:

None

get_resolver(path)[source]

Get the resolver from the node at the given path.

Parameters:

path (str) – Path to the node.

Return type:

Any

Returns:

The resolver, or None if no resolver is set.

del_item(path)[source]

Delete and return node at path.

Parameters:

path (str) – Path to the node.

Return type:

TreeStoreNode

Returns:

The removed TreeStoreNode.

Raises:

KeyError – If path not found.

pop(path, default=None)[source]

Remove and return value at path.

Parameters:
  • path (str) – Path to the node.

  • default (Any (default: None)) – Default value if path not found.

Return type:

Any

Returns:

The value of the removed node, or default.

iter_keys()[source]

Yield labels at this level in insertion order.

Return type:

Iterator[str]

iter_values()[source]

Yield values at this level in insertion order.

Return type:

Iterator[Any]

iter_items()[source]

Yield (label, value) pairs in insertion order.

Return type:

Iterator[tuple[str, Any]]

iter_nodes()[source]

Yield nodes at this level in insertion order.

Return type:

Iterator[TreeStoreNode]

keys()[source]

Return list of labels at this level in insertion order.

Return type:

list[str]

values()[source]

Return list of values at this level in insertion order.

Return type:

list[Any]

items()[source]

Return list of (label, value) pairs in insertion order.

Return type:

list[tuple[str, Any]]

nodes()[source]

Return list of nodes at this level in insertion order.

Return type:

list[TreeStoreNode]

get_nodes(path='')[source]

Get nodes at path (or root if empty).

Parameters:

path (str (default: '')) – Optional path to get nodes from.

Return type:

list[TreeStoreNode]

Returns:

List of TreeStoreNode at the specified level in insertion order.

iter_digest(what='#k,#v')[source]

Yield data from nodes using digest syntax.

Parameters:

what (str (default: '#k,#v')) – Comma-separated specifiers: - #k: labels - #v: values - #a: all attributes (dict) - #a.attrname: specific attribute

Yields:

Values, or tuples if multiple specifiers.

Return type:

Iterator[Any]

Example

>>> for label in store.iter_digest('#k'):
...     print(label)
digest(what='#k,#v')[source]

Extract data from nodes using digest syntax.

Parameters:

what (str (default: '#k,#v')) – Comma-separated specifiers: - #k: labels - #v: values - #a: all attributes (dict) - #a.attrname: specific attribute

Return type:

list[Any]

Returns:

List of values, or list of tuples if multiple specifiers.

Example

>>> store.digest('#k')  # ['label1', 'label2']
>>> store.digest('#v')  # [value1, value2]
>>> store.digest('#k,#v')  # [('label1', val1), ('label2', val2)]
>>> store.digest('#a.color')  # ['red', 'blue']
walk(callback=None, _prefix='')[source]

Walk the tree, optionally calling a callback on each node.

Parameters:
  • callback (Optional[Callable[[TreeStoreNode], Any]] (default: None)) – Optional function to call on each node. If provided, walk returns None.

  • _prefix (str (default: '')) – Internal use for path building.

Yields:

Tuples of (path, node) if no callback provided.

Return type:

Optional[Iterator[tuple[str, TreeStoreNode]]]

Example

>>> for path, node in store.walk():
...     print(path, node.value)
>>> store.walk(lambda n: print(n.label))
flattened(path_registry=None)[source]

Yield flat tuples representing the tree in depth-first order.

Converts the hierarchical tree structure into a flat sequence of tuples, suitable for serialization. Each tuple contains all information needed to reconstruct a node: its parent reference, label, tag, value, and attributes.

This method is the foundation for TYTX serialization (see to_tytx()).

Output Modes:
Normal mode (path_registry=None):

Parent references are full path strings. More readable and compresses very well with gzip due to repetitive patterns.

Compact mode (path_registry=dict):

Parent references are sequential numeric codes (0, 1, 2…). The provided dict is populated with {code: path} mappings. Produces smaller output without compression (~32% smaller), but gzip actually compresses normal mode better.

Parameters:

path_registry (dict[int, str] | None (default: None)) –

Optional dict to enable compact mode. - If None: parent is emitted as path string (normal mode) - If dict: parent is emitted as numeric code, and the dict

is populated with {code: full_path} mappings for branches

Yields:

tuple

(parent, label, tag, value, attr) where:
  • parent: Reference to parent node
    • Normal mode: path string (’’ for root-level nodes)

    • Compact mode: int code (None for root-level nodes)

  • label: Node’s unique key within its parent (e.g., ‘floor_0’)

  • tag: Node type from builder (e.g., ‘floor’) or None

  • value: Node value
    • None for branch nodes (nodes with children)

    • Actual value for leaf nodes (scalar values)

  • attr: Dict of node attributes (always a copy)

Return type:

Iterator[tuple[str | int | None, str, str | None, Any, dict[str, Any]]]

Note

  • Iteration order is depth-first (parent before children)

  • Branch nodes have value=None because their “value” is the child store

  • The path_registry dict is modified in place during iteration

  • Uses walk() internally for tree traversal

Example

Normal mode (path strings):

>>> store = TreeStore(builder=BuildingBuilder())
>>> b = store.building(name='Casa Mia')
>>> b.floor(number=1).room(name='Kitchen')
>>>
>>> for row in store.flattened():
...     parent, label, tag, value, attr = row
...     print(f"{parent!r:20} {label:15} {tag}")
''                   building_0      building
'building_0'         floor_0         floor
'building_0.floor_0' room_0          room

Compact mode (numeric codes):

>>> paths = {}
>>> for row in store.flattened(path_registry=paths):
...     parent, label, tag, value, attr = row
...     print(f"{parent!r:5} {label:15} {tag}")
None  building_0      building
0     floor_0         floor
1     room_0          room
>>>
>>> paths
{0: 'building_0', 1: 'building_0.floor_0'}

See also

  • to_tytx(): Serializes using this method

  • walk(): The underlying tree traversal method

property root: TreeStore

Get the root TreeStore of this hierarchy.

property depth: int

Get the depth of this store in the hierarchy (root=0).

property parent_node: TreeStoreNode | None

Get the parent node (alias for self.parent).

as_dict()[source]

Convert to plain dict (recursive).

Branch nodes become nested dicts with their attributes and children. Leaf nodes become their value directly (or dict with _value if has attrs).

Return type:

dict[str, Any]

Returns:

Nested dictionary representation of the tree.

clear()[source]

Remove all nodes from this store.

Does not trigger deletion events for individual nodes.

Return type:

None

update(other, ignore_none=False)[source]

Update this TreeStore with data from another source.

For each item in other: - If label exists: updates attributes, and if both are branches,

recursively updates children; otherwise replaces value

  • If label doesn’t exist: adds the new node

Parameters:
  • other (dict | list | TreeStore) – Source data (dict, list of tuples, or TreeStore)

  • ignore_none (bool (default: False)) – If True, don’t update values that are None

Return type:

None

Example

>>> store = TreeStore({'config': {'a': 1, 'b': 2}})
>>> store.update({'config': {'b': 3, 'c': 4}})
>>> store['config.a']  # 1 (preserved)
>>> store['config.b']  # 3 (updated)
>>> store['config.c']  # 4 (added)
get(label, default=None)[source]

Get node by label at this level, with default.

Unlike get_node(), this only looks at direct children (no path traversal).

Parameters:
  • label (str) – Node label to find.

  • default (Any (default: None)) – Value to return if not found.

Return type:

TreeStoreNode | None

Returns:

TreeStoreNode if found, default otherwise.

property is_valid: bool

True if all nodes in this store are valid.

Recursively checks all nodes in the tree for validation errors.

Returns:

True if no node has validation errors, False otherwise.

Example

>>> store = TreeStore(builder=HtmlBuilder())
>>> thead = store.thead()
>>> store.is_valid
False  # thead requires at least 1 tr
>>> thead.tr()
>>> store.is_valid
True
validation_errors()[source]

Return all validation errors in the tree.

Return type:

dict[str, list[str]]

Returns:

Dictionary mapping node paths to their error lists. Only includes nodes with errors.

Example

>>> store = TreeStore(builder=HtmlBuilder())
>>> thead = store.thead()
>>> store.validation_errors()
{'thead_0': ["requires at least 1 'tr', has 0"]}
to_tytx(transport=None, compact=False)[source]

Serialize TreeStore to TYTX format with type preservation.

TYTX (Typed Transport) preserves Python types (Decimal, date, datetime, time) in the serialized format, eliminating manual type conversion when deserializing.

The tree is serialized as a flat list of row tuples in depth-first order. Each row contains: (parent, label, tag, value, attr).

Parameters:
  • transport (Optional[Literal['json', 'msgpack']] (default: None)) –

    Output format: - None or ‘json’: JSON string (default). Human-readable,

    compresses very well with gzip.

    • ’msgpack’: Binary MessagePack bytes. ~30% smaller than JSON before compression.

  • compact (bool (default: False)) –

    Serialization mode: - False (default): Parent as path strings (‘a.b.c’).

    Recommended with gzip compression.

    • True: Parent as numeric codes (0, 1, 2…). ~32% smaller without compression, but gzip prefers normal.

Returns:

If transport is None or ‘json’ bytes: If transport is ‘msgpack’

Return type:

str

Raises:

ImportError – If genro-tytx package is not installed.

Example

>>> from decimal import Decimal
>>> from datetime import date
>>>
>>> store = TreeStore()
>>> store.set_item('invoice.amount', Decimal('1234.56'))
>>> store.set_item('invoice.date', date(2025, 1, 15))
>>>
>>> # Default JSON format
>>> json_data = store.to_tytx()
>>>
>>> # Binary MessagePack (smaller)
>>> msgpack_data = store.to_tytx(transport='msgpack')
>>>
>>> # Compact mode (for uncompressed transmission)
>>> compact_data = store.to_tytx(compact=True)

See also

  • from_tytx(): Deserialize back to TreeStore

  • flattened(): The underlying flat representation

classmethod from_tytx(data, transport=None, builder=None)[source]

Deserialize TreeStore from TYTX format with type preservation.

Reconstructs a TreeStore from TYTX-serialized data. Automatically detects normal vs compact format. Types (Decimal, date, datetime, time) are preserved exactly as they were before serialization.

Parameters:
  • data (str | bytes) – Serialized data from to_tytx(). - str: If transport is None or ‘json’ - bytes: If transport is ‘msgpack’

  • transport (Optional[Literal['json', 'msgpack']] (default: None)) – Input format (must match serialization): - None or ‘json’: Parse as JSON string (default) - ‘msgpack’: Parse as MessagePack bytes

  • builder (Any | None (default: None)) – Optional builder for the reconstructed store. Enables builder methods (e.g., store.div()) on the result.

Returns:

Fully reconstructed tree with:
  • Complete node hierarchy

  • All values with original types preserved

  • All node attributes restored

Return type:

TreeStore

Raises:

ImportError – If genro-tytx package is not installed.

Example

>>> # Basic round-trip
>>> original = TreeStore()
>>> original.set_item('config.price', Decimal('99.99'))
>>> data = original.to_tytx()
>>>
>>> restored = TreeStore.from_tytx(data)
>>> restored['config.price']  # Decimal('99.99'), not float
>>>
>>> # With MessagePack
>>> data = original.to_tytx(transport='msgpack')
>>> restored = TreeStore.from_tytx(data, transport='msgpack')
>>>
>>> # With builder
>>> restored = TreeStore.from_tytx(data, builder=HtmlBuilder())

See also

  • to_tytx(): Serialize TreeStore to TYTX format

classmethod from_xml(data, builder=None)[source]

Load TreeStore from XML string.

Each XML element becomes a node with:

  • label: tag name with counter suffix (element_0, element_1, etc.)

  • value: text content (leaf) or child TreeStore (branch)

  • attr: XML attributes, plus ‘_tag’ with namespace prefix if present

Namespace Handling:

XML namespaces are processed as follows:

  • Namespace declarations (xmlns:prefix=”uri”) are extracted

  • Element tags with namespaces store the prefixed form in ‘_tag’

  • The label uses only the local name (without namespace)

  • Namespaced attributes are filtered out (not preserved)

Example with namespaces:

xml = '''<root xmlns:ns="http://example.com">
    <ns:item>value</ns:item>
</root>'''
store = TreeStore.from_xml(xml)
# Label: 'item_0', attr['_tag']: 'ns:item'
Parameters:
  • data (str) – XML string to parse.

  • builder (Any | None (default: None)) – Optional builder for the resulting store.

Return type:

TreeStore

Returns:

TreeStore with XML structure. The root element becomes the first (and typically only) top-level node.

Example

Simple XML:

>>> xml = '<html><head><title>Hello</title></head></html>'
>>> store = TreeStore.from_xml(xml)
>>> store['html_0.head_0.title_0']
'Hello'

Accessing tag and attributes:

>>> xml = '<div class="main"><span id="x">text</span></div>'
>>> store = TreeStore.from_xml(xml)
>>> node = store.get_node('div_0.span_0')
>>> node.attr['id']
'x'
>>> node.value
'text'

See also

  • to_xml() - Convert TreeStore back to XML

to_xml(root_tag=None)[source]

Serialize TreeStore to XML string.

Converts the TreeStore hierarchy into an XML document. Each node becomes an XML element with:

  • tag: from node’s ‘_tag’ attribute, or label without suffix

  • attributes: node’s attr dict (excluding internal _* keys)

  • content: text value (if leaf) or child elements (if branch)

Root Element Handling:

The root element is determined as follows:

  • If root_tag is provided, it wraps all content

  • If store has exactly one top-level node and no root_tag, that node becomes the root element directly

  • If store has multiple top-level nodes and no root_tag, they are wrapped in a <root> element

Tag Resolution:

For each node, the XML tag is resolved in order:

  1. node.attr['_tag'] - Explicit tag (may include namespace prefix)

  2. node.label.rsplit('_', 1)[0] - Label without counter suffix

This allows round-trip preservation when loading from XML.

Parameters:

root_tag (str | None (default: None)) – Optional root element tag. If None and store has exactly one root node, uses that node’s tag. If None and store has multiple nodes, defaults to ‘root’.

Return type:

str

Returns:

XML string representation without XML declaration.

Example

Single root node:

>>> store = TreeStore()
>>> store.set_item('html.head.title', 'Hello')
>>> print(store.to_xml())
<html><head><title>Hello</title></head></html>

Multiple top-level nodes:

>>> store = TreeStore()
>>> store.set_item('item', 'first')
>>> store.set_item('item', 'second')
>>> print(store.to_xml())
<root><item>first</item><item>second</item></root>

With explicit root tag:

>>> store = TreeStore()
>>> store.set_item('item', 'value')
>>> print(store.to_xml(root_tag='items'))
<items><item>value</item></items>

See also

TreeStoreNode

class genro_treestore.TreeStoreNode[source]

Bases: object

A node in a TreeStore hierarchy.

Each node has: - label: The node’s unique name/key within its parent - attr: Dictionary of attributes - value: Either a scalar value or a TreeStore (for children) - parent: Reference to the containing TreeStore - tag: Optional type/tag for the node (used by builders)

Example

>>> node = TreeStoreNode('user', {'id': 1}, 'Alice')
>>> node.label
'user'
>>> node.value
'Alice'
Parameters:
__init__(label, attr=None, value=None, parent=None, tag=None, resolver=None)[source]

Initialize a TreeStoreNode.

Parameters:
  • label (str) – The node’s unique name/key.

  • attr (dict[str, Any] | None (default: None)) – Optional dictionary of attributes.

  • value (Any (default: None)) – The node’s value (scalar or TreeStore for children).

  • parent (TreeStore | None (default: None)) – The TreeStore containing this node.

  • tag (str | None (default: None)) – Optional type/tag for the node (used by builders).

  • resolver (TreeStoreResolver | None (default: None)) – Optional resolver for lazy/dynamic value computation.

Return type:

None

label
attr
parent
tag
property value: Any

Get the node’s value.

If a resolver is set, triggers resolution instead of returning the stored value.

property resolver: TreeStoreResolver | None

Get the node’s resolver.

set_value(value, trigger=True, reason=None)[source]

Set the node’s value, optionally triggering events.

Parameters:
  • value (Any) – The new value.

  • trigger (bool (default: True)) – If True, notify subscribers of the change.

  • reason (str | None (default: None)) – Optional reason string for the trigger.

Return type:

None

property is_branch: bool

True if this node contains a TreeStore (has children).

property is_leaf: bool

True if this node contains a scalar value.

get_attr(attr=None, default=None)[source]

Get attribute value or all attributes.

Parameters:
  • attr (str | None (default: None)) – Attribute name. If None, returns all attributes.

  • default (Any (default: None)) – Default value if attribute not found.

Return type:

Any

Returns:

Attribute value, default, or dict of all attributes.

set_attr(_attr=None, trigger=True, reason=None, **kwargs)[source]

Set attributes on the node.

Parameters:
  • _attr (dict[str, Any] | None (default: None)) – Dictionary of attributes to set.

  • trigger (bool (default: True)) – If True, notify subscribers of the change.

  • reason (str | None (default: None)) – Optional reason string for the trigger.

  • **kwargs (Any) – Additional attributes as keyword arguments.

Return type:

None

subscribe(subscriber_id, callback)[source]

Subscribe to changes on this specific node.

Parameters:
  • subscriber_id (str) – Unique identifier for this subscription.

  • callback (Callable[..., None]) – Function to call on changes.

Return type:

None

Callback signature:

callback(node, info, evt) - node: This TreeStoreNode - info: oldvalue (for ‘upd_value’) or list of changed attrs (for ‘upd_attr’) - evt: Event type (‘upd_value’ or ‘upd_attr’)

Example

>>> def on_change(node, info, evt):
...     print(f"{evt}: {info}")
>>> node.subscribe('watcher', on_change)
unsubscribe(subscriber_id)[source]

Unsubscribe from changes on this node.

Parameters:

subscriber_id (str) – The subscription identifier to remove.

Return type:

None

property is_valid: bool

True if this node has no validation errors.

See Also

Exceptions

InvalidChildError

exception genro_treestore.InvalidChildError[source]

Bases: TreeStoreError

Raised when a child tag is not allowed under the current parent.

This exception is raised when attempting to add a child node with a tag that violates the builder’s structural rules. Common causes:

  • Adding a child to a void/leaf element (e.g., children under <br>)

  • Tag not in the parent’s allowed children list

  • Tag explicitly excluded from the parent

Example

>>> store = TreeStore(builder=HtmlBuilder(), raise_on_error=True)
>>> ul = store.body().ul()
>>> ul.div()  # div is not a valid child of ul
InvalidChildError: 'div' is not a valid child of 'ul'

See also

MissingChildError

exception genro_treestore.MissingChildError[source]

Bases: TreeStoreError

Raised when a required child tag is missing from a parent.

This exception is raised during validation when a parent node is missing one or more mandatory children as defined by the builder’s cardinality rules (minimum count > 0).

The error is typically raised when:

  • Calling store.builder.check() explicitly

  • Using ValidationSubscriber with hard error mode

  • Finalizing a structure that requires certain children

Example

>>> # Assuming a builder requires 'head' and 'body' in 'html'
>>> store = TreeStore(builder=HtmlBuilder())
>>> html = store.html()
>>> # Missing head and body children
>>> store.builder.check(html)
MissingChildError: 'html' requires child 'head' (min: 1, found: 0)

See also

  • check()

  • ValidationSubscriber

TooManyChildrenError

exception genro_treestore.TooManyChildrenError[source]

Bases: TreeStoreError

Raised when a child tag exceeds its maximum allowed count.

This exception is raised when adding a child would exceed the maximum cardinality defined by the builder’s rules. For example, an HTML document can only have one <head> element.

Cardinality is specified using slice notation in builder definitions:

  • tag or tag[:] - unlimited (0 to infinity)

  • tag[1] - exactly one required

  • tag[0:1] - zero or one (optional, max 1)

  • tag[1:3] - between 1 and 3 inclusive

Example

>>> # Assuming 'head' has max cardinality of 1
>>> store = TreeStore(builder=HtmlBuilder(), raise_on_error=True)
>>> html = store.html()
>>> html.head()  # First head - OK
>>> html.head()  # Second head - Error
TooManyChildrenError: 'html' already has maximum 'head' children (1)

See also

  • element()

  • _parse_children_spec()