Store Module
The store package contains the core TreeStore implementation.
Module Structure
graph TB
subgraph "genro_treestore.store"
CORE[core.py<br/>TreeStore class]
NODE[node.py<br/>TreeStoreNode class]
SUB[subscription.py<br/>Event system]
SER[serialization.py<br/>TYTX format]
CORE --> NODE
CORE --> SUB
CORE --> SER
end
TreeStore
- class genro_treestore.TreeStore[source]
Bases:
SubscriptionMixinA 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
- __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:
- Returns:
Callable that creates a child via the builder.
- Raises:
AttributeError – If no builder or builder has no such method.
- 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:
- Return type:
- 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:
- 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:
- Return type:
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:
- Returns:
TreeStoreNode at the path, or None if not found.
- del_item(path)[source]
Delete and return node at path.
- get_nodes(path='')[source]
Get nodes at path (or root if empty).
- Parameters:
path (
str(default:'')) – Optional path to get nodes from.- Return type:
- 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:
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:
- 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:
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 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).
- clear()[source]
Remove all nodes from this store.
Does not trigger deletion events for individual nodes.
- Return type:
- 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:
- Return type:
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:
- Return type:
- 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:
- 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:
- 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 bytesbuilder (
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:
- 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:
- Return type:
- 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_tagis provided, it wraps all contentIf store has exactly one top-level node and no
root_tag, that node becomes the root element directlyIf 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:
node.attr['_tag']- Explicit tag (may include namespace prefix)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:
- 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
from_xml()- Load TreeStore from XML
TreeStoreNode
- class genro_treestore.TreeStoreNode[source]
Bases:
objectA 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.
- set_attr(_attr=None, trigger=True, reason=None, **kwargs)[source]
Set attributes on the node.
- Parameters:
- Return type:
- subscribe(subscriber_id, callback)[source]
Subscribe to changes on this specific node.
- Parameters:
- Return type:
- 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)
Class Relationships
classDiagram
class TreeStore {
+parent: TreeStoreNode | None
+builder: BuilderBase | None
+set_item(path, value, **attr)
+get_item(path, default)
+del_item(path)
+get_node(path)
+subscribe(name, **callbacks)
+unsubscribe(name, **events)
}
class TreeStoreNode {
+label: str
+value: Any
+attr: dict
+parent: TreeStore
+resolver: TreeStoreResolver | None
}
TreeStore "1" *-- "*" TreeStoreNode : contains
TreeStoreNode "1" --> "0..1" TreeStore : value can be
TreeStoreNode --> TreeStore : parent
TreeStore --> TreeStoreNode : parent
Path Resolution
flowchart LR
subgraph "Path Types"
DOT[a.b.c<br/>Dotted path]
POS[#0, #-1<br/>Positional]
ATTR[path?attr<br/>Attribute]
end
subgraph "Resolution"
DOT --> SPLIT[Split by dot]
POS --> INDEX[Array index]
ATTR --> GETATTR[Get attribute]
end
Subscription System
TreeStore subscription and event notification system.
This module provides the event subscription mechanism for TreeStore, enabling reactive programming patterns. Subscribers can listen to node changes (value/attribute updates), insertions, and deletions.
Events propagate up the hierarchy: a subscriber on the root TreeStore receives events from all descendants, with the path indicating where the change occurred.
- Event Types:
‘upd_value’: Node value changed
‘upd_attr’: Node attributes changed
‘ins’: Node inserted
‘del’: Node deleted
Callback Signature:
def callback(node, path, evt, oldvalue=None, index=None, reason=None):
'''
Args:
node: The affected TreeStoreNode
path: Dot-separated path from the subscribed store to the node
evt: Event type ('upd_value', 'upd_attr', 'ins', 'del')
oldvalue: Previous value (for 'upd_value' events)
index: Position index (for 'ins' and 'del' events)
reason: Optional string identifying the change source
'''
Example
>>> def on_change(node, path, evt, **kw):
... print(f"{evt} at {path}: {node.value}")
>>> store.subscribe('logger', any=on_change)
>>> store.set_item('config.debug', True)
ins at config.debug: True
- class genro_treestore.store.subscription.SubscriptionMixin[source]
Bases:
objectMixin class providing subscription functionality for TreeStore.
This mixin adds subscribe/unsubscribe methods and event notification to TreeStore. It requires the host class to have: - _upd_subscribers: dict[str, SubscriberCallback] - _ins_subscribers: dict[str, SubscriberCallback] - _del_subscribers: dict[str, SubscriberCallback] - parent: TreeStoreNode | None
- parent: TreeStoreNode | None
- subscribe(subscriber_id, update=None, insert=None, delete=None, any=None)[source]
Subscribe to change events on this store.
Events propagate up the hierarchy: a subscriber on the root receives events from all descendants, with the path indicating where the change occurred.
- Parameters:
subscriber_id (
str) – Unique identifier for this subscription.update (
Optional[Callable[...,None]] (default:None)) – Callback for value/attribute changes.insert (
Optional[Callable[...,None]] (default:None)) – Callback for node insertions.delete (
Optional[Callable[...,None]] (default:None)) – Callback for node deletions.any (
Optional[Callable[...,None]] (default:None)) – Callback for all events (shorthand for update+insert+delete).
- Return type:
Callback Signature:
def callback(node, path, evt, oldvalue=None, index=None, reason=None): ''' Args: node: The affected TreeStoreNode path: Dot-separated path from this store to the node evt: Event type ('upd_value', 'upd_attr', 'ins', 'del') oldvalue: Previous value (for 'upd_value' events) index: Position index (for 'ins' and 'del' events) reason: Optional string identifying the change source '''
Example
>>> def on_change(node, path, evt, **kw): ... print(f"{evt} at {path}") >>> store.subscribe('renderer', any=on_change)
- unsubscribe(subscriber_id, update=False, insert=False, delete=False, any=False)[source]
Unsubscribe from change events.
- Parameters:
subscriber_id (
str) – The subscription identifier to remove.update (
bool(default:False)) – Unsubscribe from update events.insert (
bool(default:False)) – Unsubscribe from insert events.delete (
bool(default:False)) – Unsubscribe from delete events.any (
bool(default:False)) – Unsubscribe from all events.
- Return type:
Event Types
Event |
Constant |
Description |
|---|---|---|
Insert |
|
Node created |
Delete |
|
Node removed |
Update Value |
|
Value changed |
Update Attribute |
|
Attribute changed |
Serialization
TreeStore serialization - TYTX format support.
This module provides functions to serialize and deserialize TreeStore hierarchies to/from TYTX format. TYTX preserves Python types (Decimal, date, datetime, time) across serialization, eliminating manual type conversion on both ends.
- Wire Format:
The serialized format is a dict with: - ‘rows’: List of tuples, one per node in depth-first order - ‘paths’: (optional, compact mode only) Registry mapping codes to paths
Each row tuple contains: (parent, label, tag, value, attr) - parent: Parent path string (e.g., ‘a.b’) or numeric code if compact - label: Node’s unique key within its parent (e.g., ‘div_0’) - tag: Node type from builder (e.g., ‘div’) or None - value: Node value (None for branches, actual value for leaves) - attr: Dict of node attributes
- Two Serialization Modes:
- Normal mode (compact=False, default):
Parent references are full path strings. More readable, compresses well with gzip due to repetitive path patterns.
- Compact mode (compact=True):
Parent references are numeric codes (0, 1, 2…). Smaller without compression, but gzip actually makes it larger than normal mode.
- Recommendation:
Use normal mode (default) if you’ll compress the data
Use compact mode only for uncompressed transmission
- Datetime Handling:
TYTX serializes all datetimes as UTC with millisecond precision. Naive datetimes are treated as UTC on serialization. On deserialization, datetimes are always returned as timezone-aware (UTC).
For roundtrip comparison, use
genro_tytx.utils.tytx_equivalent()which handles naive vs aware UTC equivalence.- Requirements:
Requires the genro-tytx package for type-preserving encoding/decoding. Install with: pip install genro-tytx
Example
>>> from genro_treestore import TreeStore
>>> from decimal import Decimal
>>>
>>> store = TreeStore()
>>> store.set_item('invoice.amount', Decimal('1234.56'))
>>> store.set_item('invoice.paid', False)
>>>
>>> # Serialize
>>> data = store.to_tytx()
>>>
>>> # Deserialize - types are preserved
>>> restored = TreeStore.from_tytx(data)
>>> restored['invoice.amount'] # Decimal('1234.56'), not string
- genro_treestore.store.serialization.to_tytx(store, transport=None, compact=False)[source]
Serialize a TreeStore to TYTX format.
Converts the entire tree hierarchy into a flat list of row tuples, then encodes it using TYTX which preserves Python types (Decimal, date, datetime, time) in the wire format.
- Parameters:
store (
TreeStore) – The TreeStore to serialize.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, good for bandwidth-constrained scenarios.
compact (
bool(default:False)) –Serialization mode: - False (default): Parent paths as full strings (‘a.b.c’).
Recommended when using gzip compression.
True: Parent paths as numeric codes (0, 1, 2…). ~30% smaller uncompressed, but larger after gzip.
- Return type:
- Returns:
str if transport is None or ‘json’, bytes if ‘msgpack’.
- Raises:
ImportError – If genro-tytx package is not installed.
- Output Format:
Normal mode:
{"rows": [ ["", "root", "div", null, {"id": "main"}], ["root", "child_0", "span", "text", {}], ... ]}
Compact mode:
{"rows": [ [null, "root", "div", null, {"id": "main"}], [0, "child_0", "span", "text", {}], ... ], "paths": {"0": "root", ...}}
Example
>>> store = TreeStore() >>> store.set_item('config.timeout', 30) >>> store.set_item('config.retry', True) >>> >>> # Default JSON >>> json_data = to_tytx(store) >>> >>> # Binary MessagePack >>> msgpack_data = to_tytx(store, transport='msgpack') >>> >>> # Compact mode (smaller without gzip) >>> compact_data = to_tytx(store, compact=True)
- genro_treestore.store.serialization.from_tytx(data, transport=None, builder=None)[source]
Deserialize TreeStore from TYTX format.
Reconstructs a complete TreeStore hierarchy from TYTX-encoded data. Automatically detects whether the data uses normal or compact format by checking for the presence of a ‘paths’ registry.
- The reconstruction algorithm:
Decode TYTX data to get rows and optional path registry
For each row in depth-first order: - Resolve parent reference (path string or numeric code) - Create TreeStoreNode with label, tag, value, and attributes - If value is None, create child TreeStore (branch node) - Insert node into parent store
- 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 matching how data was serialized: - None or ‘json’: Parse as JSON string (default) - ‘msgpack’: Parse as MessagePack bytesbuilder (
Any|None(default:None)) – Optional builder instance for the reconstructed store. If provided, enables builder pattern methods on the result.
- Returns:
- Fully reconstructed tree with:
All nodes in correct hierarchy
Original values with types preserved (Decimal, date, etc.)
All node attributes restored
Parent-child relationships established
- Return type:
- Raises:
ImportError – If genro-tytx package is not installed.
ValueError – If data format is invalid or corrupted.
- Format Detection:
The function automatically handles both serialization modes:
Normal mode (no ‘paths’ key):
{"rows": [["", "root", "div", null, {}], ...]}
Compact mode (has ‘paths’ key):
{"rows": [[null, "root", "div", null, {}], ...], "paths": {"0": "root", ...}}
Example
>>> # Basic deserialization >>> store = from_tytx(json_data) >>> store['config.timeout'] 30
>>> # With MessagePack >>> store = from_tytx(msgpack_data, transport='msgpack')
>>> # With builder for DOM-like API >>> store = from_tytx(data, builder=HtmlBuilder()) >>> store.div() # Builder methods available
>>> # Round-trip preserves types >>> original = TreeStore() >>> original.set_item('price', Decimal('99.99')) >>> data = to_tytx(original) >>> restored = from_tytx(data) >>> restored['price'] # Decimal('99.99'), not float
See Also
Quick Start - Getting started guide
Path Syntax - Path navigation details
Subscriptions - Event subscription guide