Hierarchical data structures with builder pattern support for Genro Kyō
Project description
Genro-TreeStore
A lightweight hierarchical data structure with builder pattern support for the Genro ecosystem (Genro Kyo).
TreeStore provides a powerful tree-based container with O(1) path lookup, reactive subscriptions, lazy value resolution, and schema-driven builders for generating validated hierarchical structures like HTML, XML, or custom DSLs.
Architecture Overview
graph TB
subgraph "TreeStore Core"
TS[TreeStore<br/>Container]
TSN[TreeStoreNode<br/>Node wrapper]
TS -->|contains| TSN
TSN -->|value is| TS2[TreeStore<br/>Branch]
TSN -->|or| VAL[Leaf Value]
TSN -->|parent ref| TS
TS2 -->|parent ref| TSN
end
subgraph "Features"
RES[Resolvers<br/>Lazy values]
SUB[Subscriptions<br/>Reactive events]
BLD[Builders<br/>Typed APIs]
VAL2[Validation<br/>Structure rules]
end
TS --> RES
TS --> SUB
TS --> BLD
BLD --> VAL2
Installation
pip install genro-treestore
Key Features
| Feature | Description |
|---|---|
| O(1) Lookup | Direct path-based access via internal index |
| Builder Pattern | Fluent APIs with auto-labeling and validation |
| Reactive Subscriptions | Event propagation for insert/update/delete |
| Lazy Resolvers | Dynamic value computation with TTL caching |
| Schema Builders | Generate builders from RNC or XSD schemas |
| Type-Safe Serialization | TYTX format preserves Decimal, date, datetime |
Quick Start
Basic TreeStore Usage
from genro_treestore import TreeStore
store = TreeStore()
# Create nested structure (auto-creates intermediate nodes)
store.set_item('config.database.host', 'localhost')
store.set_item('config.database.port', 5432)
store.set_item('config.debug', True)
# Access values
print(store['config.database.host']) # 'localhost'
print(store['config.debug']) # True
# Iterate over children
for key, value in store['config.database'].items():
print(f"{key}: {value}")
# host: localhost
# port: 5432
Path Syntax
TreeStore supports a rich path syntax for navigation:
graph LR
subgraph "Path Syntax"
A["store['a.b.c']"] -->|Dotted path| B[Nested access]
C["store['#0']"] -->|Positional| D[First child]
E["store['#-1']"] -->|Negative| F[Last child]
G["store['path?attr']"] -->|Attribute| H[Get attribute]
end
| Syntax | Description | Example |
|---|---|---|
'a.b.c' |
Dotted path | store['config.database.host'] |
'#N' |
Position index | store['users.#0'] (first child) |
'#-N' |
Negative index | store['users.#-1'] (last child) |
'path?attr' |
Attribute access | store['div?class'] |
Builder Pattern with HTML
from genro_treestore import TreeStore
from genro_treestore.builders import HtmlBuilder
store = TreeStore(builder=HtmlBuilder())
# Fluent API with auto-labeling
body = store.body()
div = body.div(id='main', class_='container')
div.h1(value='Welcome')
div.p(value='Hello, World!')
ul = div.ul()
ul.li(value='Item 1')
ul.li(value='Item 2')
ul.li(value='Item 3')
# Access by auto-generated labels
print(store['body_0.div_0.h1_0']) # 'Welcome'
print(store['body_0.div_0?id']) # 'main'
Dynamic Builders from Schemas
From RNC (RELAX NG Compact)
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 to TreeStore.')
From XSD (XML Schema)
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')
invoice.Header().Number(value='001')
Resolvers: Lazy Value Computation
sequenceDiagram
participant C as Client
participant S as TreeStore
participant N as Node
participant R as Resolver
C->>S: store['config.computed']
S->>N: get value
N->>R: load()
R-->>R: compute value
R->>N: return + cache
N-->>S: value
S-->>C: value
Note over R: Cached until TTL expires
from genro_treestore import TreeStore, CallbackResolver
store = TreeStore()
store.set_item('config.base_url', 'https://api.example.com')
store.set_item('config.version', 'v2')
# Dynamic computed value
def get_full_url(node):
parent = node.parent
base = parent.get_item('base_url')
version = parent.get_item('version')
return f"{base}/{version}"
store.set_item('config.full_url')
store.set_resolver('config.full_url', CallbackResolver(get_full_url))
print(store['config.full_url']) # 'https://api.example.com/v2'
# With caching (60 seconds TTL)
store.set_resolver('config.full_url',
CallbackResolver(get_full_url, cache_time=60))
Built-in Resolvers
| Resolver | Description |
|---|---|
CallbackResolver |
Compute value via callback function |
DirectoryResolver |
Lazy-load directory contents |
TxtDocResolver |
Load text file content |
Reactive Subscriptions
graph BT
subgraph "Event Propagation"
LEAF[Leaf Node<br/>value changed]
PARENT[Parent Store]
ROOT[Root Store<br/>subscriber]
LEAF -->|"upd_value"| PARENT
PARENT -->|propagate| ROOT
end
ROOT -->|callback| CB[Handler Function]
from genro_treestore import TreeStore
store = TreeStore()
def on_change(node, path, evt, **kw):
print(f"{evt}: {path} = {node.value}")
# Subscribe to all events
store.subscribe('logger', any=on_change)
store.set_item('users.alice', 'Alice')
# Output: ins: users.alice = Alice
store.set_item('users.alice', 'Alicia')
# Output: upd_value: users.alice = Alicia
store.del_item('users.alice')
# Output: del: users.alice = Alicia
store.unsubscribe('logger', any=True)
Event Types
| Event | Description |
|---|---|
ins |
Node inserted |
del |
Node deleted |
upd_value |
Node value changed |
upd_attr |
Node attribute changed |
Validation with Builders
graph TD
subgraph "Validation Flow"
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
end
from genro_treestore.builders import BuilderBase, element, valid_children
class DocBuilder(BuilderBase):
@element
@valid_children('section[1:]') # At least one section required
def document(self, store, parent, **attrs):
pass
@element
@valid_children(
'title[1]', # Exactly one title required
'para[0:]', # Zero or more paragraphs
)
def section(self, store, parent, **attrs):
pass
@element
def title(self, store, parent, value=None, **attrs):
pass
@element
def para(self, store, parent, value=None, **attrs):
pass
Cardinality Syntax
| Syntax | Meaning |
|---|---|
tag or tag[:] |
Zero or more (unlimited) |
tag[1] |
Exactly one required |
tag[0:1] |
Zero or one (optional) |
tag[1:] |
One or more required |
tag[2:5] |
Between 2 and 5 |
Serialization (TYTX)
TreeStore supports type-preserving serialization:
from decimal import Decimal
from datetime import date, datetime
from genro_treestore import TreeStore
store = TreeStore()
store.set_item('invoice.amount', Decimal('1234.56'))
store.set_item('invoice.date', date(2025, 1, 15))
store.set_item('invoice.timestamp', datetime.now())
store.set_item('invoice.paid', False)
# Serialize to JSON (types preserved as metadata)
json_data = store.to_tytx()
# Deserialize - types restored exactly
restored = TreeStore.from_tytx(json_data)
assert isinstance(restored['invoice.amount'], Decimal)
assert isinstance(restored['invoice.date'], date)
# Binary format (MessagePack - more compact)
binary_data = store.to_tytx(transport='msgpack')
restored = TreeStore.from_tytx(binary_data, transport='msgpack')
XML Support
from genro_treestore import TreeStore
# Parse XML
xml = '''<html>
<head><title>Hello</title></head>
<body><div id="main">Content</div></body>
</html>'''
store = TreeStore.from_xml(xml)
print(store['html_0.body_0.div_0']) # 'Content'
print(store['html_0.body_0.div_0?id']) # 'main'
# Generate XML
output = store.to_xml()
Module Structure
graph TB
subgraph "genro_treestore"
INIT[__init__.py<br/>Public API]
subgraph "store/"
CORE[core.py<br/>TreeStore]
NODE[node.py<br/>TreeStoreNode]
SUB[subscription.py<br/>Events]
SER[serialization.py<br/>TYTX]
end
subgraph "builders/"
BASE[base.py<br/>BuilderBase]
HTML[html.py<br/>HtmlBuilder]
RNC[rnc/<br/>RncBuilder]
XSD[xsd/<br/>XsdBuilder]
end
subgraph "resolvers/"
RBASE[base.py<br/>TreeStoreResolver]
DIR[directory.py<br/>DirectoryResolver]
end
EXC[exceptions.py]
VAL[validation.py]
end
INIT --> CORE
INIT --> BASE
INIT --> RBASE
CORE --> NODE
CORE --> SUB
CORE --> SER
BASE --> HTML
BASE --> RNC
BASE --> XSD
Development
# Clone repository
git clone https://github.com/genropy/genro-treestore.git
cd genro-treestore
# Install dev dependencies
pip install -e ".[test,dev,docs]"
# Run tests
pytest tests/
# Run tests with coverage
pytest tests/ --cov=src/genro_treestore --cov-report=term-missing
# Lint
ruff check src/
# Format
black src/
# Build docs
cd docs && make html
Documentation
Full documentation is available at genro-treestore.readthedocs.io.
License
Apache License 2.0 - See LICENSE for details.
Copyright 2025 Softwell S.r.l. - Genropy Team
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file genro_treestore-0.2.0.tar.gz.
File metadata
- Download URL: genro_treestore-0.2.0.tar.gz
- Upload date:
- Size: 134.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7dc3775abf93852b8a68bd8dc628ed6c7509d729dad4056069b5f0c502e1c7d0
|
|
| MD5 |
c3ecddb33cc72b33db096c6be1d18a15
|
|
| BLAKE2b-256 |
c2176d35f167d390bd217b9b487ec4f970420868bfd5324f4ebeaf4d6e25cdd8
|
Provenance
The following attestation bundles were made for genro_treestore-0.2.0.tar.gz:
Publisher:
publish.yml on genropy/genro-treestore
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
genro_treestore-0.2.0.tar.gz -
Subject digest:
7dc3775abf93852b8a68bd8dc628ed6c7509d729dad4056069b5f0c502e1c7d0 - Sigstore transparency entry: 787319363
- Sigstore integration time:
-
Permalink:
genropy/genro-treestore@80baaf3bf10434e972e1691e4cdc9a51a7475e16 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@80baaf3bf10434e972e1691e4cdc9a51a7475e16 -
Trigger Event:
push
-
Statement type:
File details
Details for the file genro_treestore-0.2.0-py3-none-any.whl.
File metadata
- Download URL: genro_treestore-0.2.0-py3-none-any.whl
- Upload date:
- Size: 83.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6092655209d5baf7c6c0951a4019fa7c81f94121a9faec633843d164f9a9563
|
|
| MD5 |
472344dbc4e0fcddd541706eb95cf1c0
|
|
| BLAKE2b-256 |
f47fc29b8c11bdf6e1aa0feb75fed463b308025dc0262c4b9c378d3c23b49cee
|
Provenance
The following attestation bundles were made for genro_treestore-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on genropy/genro-treestore
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
genro_treestore-0.2.0-py3-none-any.whl -
Subject digest:
d6092655209d5baf7c6c0951a4019fa7c81f94121a9faec633843d164f9a9563 - Sigstore transparency entry: 787319364
- Sigstore integration time:
-
Permalink:
genropy/genro-treestore@80baaf3bf10434e972e1691e4cdc9a51a7475e16 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@80baaf3bf10434e972e1691e4cdc9a51a7475e16 -
Trigger Event:
push
-
Statement type: