Structural Object Embodiment for Python - Transform skeletal templates into fully realized, type-safe objects through recursive hydration.
Embody is a powerful library for templated object generation that goes beyond simple string formatting. It provides structural embodiment - the recursive hydration of nested data structures with type preservation, cycle detection, and intelligent traversal strategies.
- π― Type Preservation: Variables like
${count}preserve their types (int, bool, list, dict) instead of converting everything to strings - π Multiple Strategies: Choose between recursive visitor, compiled path, or iterative stack traversal
- π¨ Multiple Syntaxes: Support for
${var},{var}, and[[var]]template syntaxes - π‘οΈ Cycle Detection: Automatically detect and prevent circular references
- π Path Addressing: Access nested data via JSON Pointer, dot notation, or tuple paths
- πΊοΈ Mapping Interfaces: Uniform interfaces for attribute access, flattening, and path-based operations
- β‘ Performance: Auto-selects optimal strategy based on template complexity
- π Type Safety: Full integration with Pydantic and other validation libraries
pip install embodyfrom embody import embody
# Simple template embodiment
template = {
'name': '${name}',
'age': '${age}',
'greeting': 'Hello ${name}!'
}
result = embody(template, {'name': 'Alice', 'age': 30})
# {'name': 'Alice', 'age': 30, 'greeting': 'Hello Alice!'}Unlike traditional templating, embody preserves types:
template = {
'count': '${num}', # Will be int, not string!
'active': '${flag}', # Will be bool
'items': '${list}', # Will be list
'message': 'Count: ${num}' # Will be string (interpolation)
}
result = embody(template, {
'num': 42,
'flag': True,
'list': [1, 2, 3]
})
assert isinstance(result['count'], int) # True - type preserved!
assert isinstance(result['active'], bool) # True
assert isinstance(result['items'], list) # TrueEmbody handles deep nesting naturally:
template = {
'user': {
'profile': {
'name': '${name}',
'settings': {
'theme': '${theme}',
'notifications': '${notify}'
}
}
}
}
result = embody(template, {
'name': 'Bob',
'theme': 'dark',
'notify': True
})from embody import Context
import datetime
ctx = Context({
'now': lambda: datetime.datetime.now(),
'user': 'Alice'
})
# Callables are invoked on access
print(ctx['now']) # Current time
print(ctx['user']) # 'Alice'from embody.mappings import AttributeMapping, PathMapping
# Attribute access (Box pattern)
attr_map = AttributeMapping({'user': {'name': 'Alice'}})
print(attr_map.user.name) # 'Alice'
# Path-based access
path_map = PathMapping({'a': {'b': {'c': 42}}})
print(path_map['a.b.c']) # Dot notation
print(path_map['/a/b/c']) # JSON Pointer
print(path_map[('a', 'b', 'c')]) # Tuple pathfrom embody.paths import JSONPointer, resolve_path
data = {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}
# JSON Pointer (RFC 6901)
ptr = JSONPointer('/users/0/name')
print(ptr.resolve(data)) # 'Alice'
# Convenience function
print(resolve_path(data, 'users.0.name')) # 'Alice'from embody import Embodier
template = {'data': '${value}'}
# Recursive visitor (default, best for one-off templates)
embodier1 = Embodier(template, strategy='recursive')
# Compiled path (best for repeated embodiment)
embodier2 = Embodier(template, strategy='compiled')
# Auto-select based on template complexity
embodier3 = Embodier(template, strategy='auto')
result = embodier1({'value': 42})from embody import embody
import os
config_template = {
'database': {
'host': '${db_host}',
'port': '${db_port}',
'name': '${db_name}'
},
'api': {
'base_url': '${api_url}',
'timeout': '${timeout}'
}
}
config = embody(config_template, {
'db_host': os.getenv('DB_HOST', 'localhost'),
'db_port': int(os.getenv('DB_PORT', '5432')),
'db_name': 'myapp',
'api_url': 'https://api.example.com',
'timeout': 30
})from embody import Embodier
response_template = {
'status': 'success',
'data': {
'user_id': '${id}',
'name': '${name}',
'timestamp': '${ts}'
}
}
embodier = Embodier(response_template, strategy='compiled')
# Efficiently generate many responses
for user in users:
response = embodier({
'id': user.id,
'name': user.name,
'ts': datetime.now()
})The original Templater API is fully preserved:
from embody.templater import Templater
template = {
'hello': '{name}',
'how are you': ['{verb}', 2, '{name} and {verb} again']
}
g = Templater.template_func(template=template)
result = g(name='NAME', verb="VERB")
# {'hello': 'NAME', 'how are you': ['VERB', 2, 'NAME and VERB again']}Contributions are welcome! Please see our contributing guidelines.
MIT License - see LICENSE file for details.