Essential utilities for the Genro ecosystem
Part of Genro Kyō ecosystem.
A lightweight, zero-dependency Python library providing core utilities that can be used across all Genro projects.
pip install genro-toolbox- SmartOptions - Multi-source config with merge via
+operator - TreeDict - Hierarchical dict with dot notation and path access
- extract_kwargs - Decorator to group kwargs by prefix
- get_uuid - Sortable 22-char unique identifiers for distributed systems
- smartasync - Unified sync/async API with automatic context detection
- safe_is_instance - isinstance() without importing the class
- render_ascii_table - ASCII table rendering with formatting
- render_markdown_table - Markdown table rendering
- tags_match - Boolean expression matcher for tag-based filtering
Load config from multiple sources with type conversion:
from genro_toolbox import SmartOptions
import sys
def serve(host: str = '127.0.0.1', port: int = 8000, debug: bool = False):
pass
# Load from env and argv with automatic type conversion
# Priority: defaults < env < argv
config = SmartOptions(serve, env='MYAPP', argv=sys.argv[1:])
config["host"] # from env (MYAPP_HOST) or default
config["port"] # int from env (MYAPP_PORT) or argv
config["debug"] # True if --debug passedCompose with + for file configs:
config = (
SmartOptions('config.yaml') + # file config
SmartOptions(serve, env='MYAPP', argv=sys.argv[1:]) # defaults < env < argv
)Load from files (YAML, JSON, TOML, INI):
opts = SmartOptions('config.yaml')
opts["server.host"] # nested dicts become SmartOptions
opts["middleware.cors"] # string lists become feature flags (True)
opts["apps.shop.module"] # list of dicts indexed by first keyBasic merge with filtering:
opts = SmartOptions(
{"timeout": 30}, # runtime values
{"timeout": 10, "retries": 3}, # defaults
ignore_none=True,
ignore_empty=True,
)
opts["timeout"] # 30 (runtime wins)
opts["retries"] # 3 (from defaults)Hierarchical dictionary with path access:
from genro_toolbox import TreeDict
# Create from nested dict
td = TreeDict({"user": {"name": "Alice", "prefs": {"theme": "dark"}}})
# Or from JSON string
td = TreeDict('{"user": {"name": "Alice"}}')
# Or from config file (JSON, YAML, TOML, INI)
td = TreeDict.from_file("config.yaml")
# Path string access
td["user.name"] # "Alice"
td["user.prefs.theme"] # "dark"
td["missing"] # None (no KeyError)
# Auto-create intermediate dicts on write
td["settings.db.host"] = "localhost"
td["settings.db.host"] # "localhost"
# List access with #N syntax
td = TreeDict({"users": [{"name": "Alice"}, {"name": "Bob"}]})
td["users.#0.name"] # "Alice"
td["users.#1.name"] # "Bob"
# Walk all paths
for path, value in td.walk():
print(f"{path} = {value}")
# users.#0.name = Alice
# users.#1.name = Bob
# Thread-safe access (sync)
with td:
td["counter"] = td["counter"] + 1
# Async-safe access
async with td:
td["counter"] = td["counter"] + 1
# Convert back to dict
td.as_dict() # {"user": {"name": "Alice", ...}}from genro_toolbox import extract_kwargs
@extract_kwargs(logging=True, cache=True)
def my_function(name, logging_kwargs=None, cache_kwargs=None, **kwargs):
print(f"logging: {logging_kwargs}")
print(f"cache: {cache_kwargs}")
my_function(
"test",
logging_level="INFO",
logging_format="json",
cache_ttl=300,
)
# logging: {'level': 'INFO', 'format': 'json'}
# cache: {'ttl': 300}from genro_toolbox import safe_is_instance
# Check type without importing
safe_is_instance(42, "builtins.int") # True
safe_is_instance(my_obj, "mypackage.BaseClass") # True (includes subclasses)from genro_toolbox import render_ascii_table, render_markdown_table
data = {
"headers": [
{"name": "Name", "type": "str"},
{"name": "Active", "type": "bool"},
],
"rows": [
["Alice", "yes"],
["Bob", "no"],
],
}
print(render_ascii_table(data))
# +-------+--------+
# |Name |Active |
# +-------+--------+
# |Alice |true |
# +-------+--------+
# |Bob |false |
# +-------+--------+from genro_toolbox import tags_match
# Simple tag check
tags_match("admin", {"admin", "user"}) # True
# OR (comma, pipe, or keyword)
tags_match("admin,public", {"public"}) # True
tags_match("admin or public", {"admin"}) # True
# AND (ampersand or keyword)
tags_match("admin&internal", {"admin", "internal"}) # True
tags_match("admin and internal", {"admin"}) # False
# NOT (exclamation or keyword)
tags_match("!admin", {"public"}) # True
tags_match("not admin", {"admin"}) # False
# Complex expressions
tags_match("(admin|public)&!internal", {"admin"}) # True
tags_match("(admin or public) and not internal", {"admin", "internal"}) # Falsefrom genro_toolbox import get_uuid
# Generate sortable unique identifiers
uid = get_uuid() # e.g., "Z00005KmLxHj7F9aGbCd3e"
len(uid) # 22 characters
uid[0] # 'Z' (version marker, sorts after legacy UUIDs)
uid.isalnum() # True (URL-safe)
# IDs are lexicographically sortable by creation time
ids = [get_uuid() for _ in range(3)]
sorted(ids) == ids # True (already sorted)from genro_toolbox import smartasync
class DataManager:
@smartasync
async def fetch_data(self, url: str):
async with httpx.AsyncClient() as client:
return await client.get(url).json()
manager = DataManager()
# Sync context - no await needed!
data = manager.fetch_data("https://api.example.com")
# Async context - use await
async def main():
data = await manager.fetch_data("https://api.example.com")
# Also works with sync methods in async context (offloaded to thread)
class LegacyProcessor:
@smartasync
def cpu_intensive(self, data):
return process(data) # Blocking operation
async def main():
proc = LegacyProcessor()
result = await proc.cpu_intensive(data) # Won't block event loopIf you write a generic helper that could be useful elsewhere, put it in genro-toolbox.
This library serves as the foundation for utilities shared across:
- genro-asgi
- genro-routes
- genro-api
- Other Genro Kyō projects
Apache License 2.0 - See LICENSE for details.
Copyright 2025 Softwell S.r.l.
