Name | grimoire-model JSON |
Version |
0.2.0
JSON |
| download |
home_page | None |
Summary | Dict-like model system with schema validation, derived fields, and inheritance for Grimoire |
upload_time | 2025-09-12 07:29:56 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.8 |
license | MIT |
keywords |
gaming
rpg
tabletop
model
validation
schema
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Grimoire Model
[](https://github.com/wyrdbound/grimoire-model/actions)
[](https://www.python.org/downloads/)
[](LICENSE)
[](htmlcov/index.html)
**Dict-like model system with schema validation, derived fields, and inheritance for the Grimoire tabletop RPG engine.**
Grimoire Model provides a sophisticated, schema-driven model system that combines the familiar dict-like interface with powerful features like automatic field derivation, template-based expressions, model inheritance, and comprehensive validation. Designed to integrate seamlessly with `grimoire-context` for complete game state management.
## โจ Features
- **๐ Dict-like Interface**: Familiar Python dictionary operations with schema validation
- **๐ Reactive Derived Fields**: Automatic computation with dependency tracking and batch updates
- **๐งฌ Model Inheritance**: Multiple inheritance support with automatic namespace-based resolution
- **๐ Template Expressions**: Jinja2-powered field templates for dynamic content
- **๐ท๏ธ Namespace Organization**: Global model registry with namespace-based organization
- **๐ก๏ธ Schema Validation**: Pydantic-based type checking and custom validation rules
- **๐ง Dependency Injection**: Pluggable resolvers for extensibility
- **โก Performance Optimized**: Efficient batch updates and lazy evaluation
- **๐ฏ grimoire-context Integration**: Seamless interoperability with context management
## ๐ Quick Start
### Installation
```bash
pip install grimoire-model
```
### Basic Usage
```python
from grimoire_model import ModelDefinition, AttributeDefinition, create_model
# Define a character model schema
character_def = ModelDefinition(
id="character",
name="Player Character",
namespace="rpg", # Organize models in namespaces
attributes={
"name": AttributeDefinition(type="str", required=True),
"level": AttributeDefinition(type="int", default=1),
"hp": AttributeDefinition(type="int", default=100),
"mp": AttributeDefinition(type="int", default=50),
# Derived fields automatically update when dependencies change
"max_hp": AttributeDefinition(
type="int",
derived="{{ level * 8 + hp }}"
),
"character_summary": AttributeDefinition(
type="str",
derived="Level {{ level }} {{ name }} ({{ max_hp }} HP, {{ mp }} MP)"
)
}
)
# Create a character instance
character = create_model(character_def, {
"name": "Aragorn",
"level": 15,
"hp": 120,
"mp": 80
})
# Dict-like interface with automatic derived field updates
print(character['name']) # "Aragorn"
print(character['max_hp']) # 240 (15 * 8 + 120)
print(character['character_summary']) # "Level 15 Aragorn (240 HP, 80 MP)"
# Updates trigger automatic recalculation
character['level'] = 20
print(character['max_hp']) # 280 (20 * 8 + 120, automatically updated)
```
### Global Model Registry
Models are automatically registered in a global registry using namespaces:
```python
from grimoire_model import get_model
# Models auto-register when created
character_def = ModelDefinition(
id="character",
namespace="rpg", # Registered in "rpg" namespace
# ... attributes ...
)
# Retrieve from anywhere in your application
retrieved_def = get_model("rpg", "character")
new_character = create_model(retrieved_def, {"name": "Hero"})
# Perfect for inheritance - child models automatically find parents
base_def = ModelDefinition(id="base", namespace="rpg", ...)
child_def = ModelDefinition(id="child", namespace="rpg", extends=["base"], ...)
# No manual registry needed - inheritance resolves automatically!
```
### Model Inheritance with Namespaces
```python
from grimoire_model import get_model, clear_registry
# Base entity definition (auto-registered in namespace)
base_entity_def = ModelDefinition(
id="base_entity",
name="Base Entity",
namespace="game", # Registered in "game" namespace
attributes={
"id": AttributeDefinition(type="str", required=True),
"name": AttributeDefinition(type="str", required=True),
"description": AttributeDefinition(type="str", default="")
}
)
# Character extends base entity (automatic inheritance resolution)
character_def = ModelDefinition(
id="character",
name="Character",
namespace="game", # Same namespace enables automatic inheritance
extends=["base_entity"], # Automatically finds base_entity in namespace
attributes={
"level": AttributeDefinition(type="int", default=1),
"hp": AttributeDefinition(type="int", default=100),
"max_hp": AttributeDefinition(
type="int",
derived="{{ level * 8 + hp }}"
)
}
)
# Create character with inherited fields (no registry needed!)
character = create_model(
character_def,
{
"id": "char_001", # From base_entity
"name": "Legolas", # From base_entity
"description": "Elf archer", # From base_entity
"level": 12, # From character
"hp": 96 # From character
}
)
print(character['id']) # "char_001" (inherited)
print(character['name']) # "Legolas" (inherited)
print(character['max_hp']) # 192 (derived field)
# Retrieve models from global registry
retrieved_char_def = get_model("game", "character")
another_character = create_model(retrieved_char_def, {
"id": "char_002",
"name": "Gimli"
})
```
### Integration with grimoire-context
```python
from grimoire_context import GrimoireContext
# Create context with character model
context = GrimoireContext({
'party': {
'leader': character,
'members': 4
}
})
# Modify character through context - derived fields update automatically
context = context.set_variable('party.leader.level', 25)
updated_character = context.get_variable('party.leader')
print(updated_character['level']) # 25
print(updated_character['max_hp']) # 296 (automatically recalculated)
```
### Batch Updates for Performance
```python
# Batch multiple changes for better performance
character.batch_update({
'level': 30,
'hp': 150,
'mp': 120
})
# All derived fields updated once after batch completion
print(character['max_hp']) # 390 (30 * 8 + 150)
```
## ๐ Documentation
- **[Logging Configuration](LOGGING.md)** - Configure library logging output and integration
## ๐ Core Concepts
### Model Definitions
Model definitions are schemas that describe the structure, types, and behavior of your data:
```python
model_def = ModelDefinition(
id="weapon",
name="Weapon",
namespace="combat", # Organize in combat namespace
description="Combat weapon with damage calculations",
attributes={
"name": AttributeDefinition(type="str", required=True),
"base_damage": AttributeDefinition(type="int", default=1, range="1..50"),
"enhancement": AttributeDefinition(type="int", default=0, range="0..10"),
# Derived field with complex logic
"total_damage": AttributeDefinition(
type="int",
derived="{{ base_damage + enhancement * 2 }}"
),
"damage_category": AttributeDefinition(
type="str",
derived="{% if total_damage >= 20 %}High{% elif total_damage >= 10 %}Medium{% else %}Low{% endif %}"
)
},
validations=[
ValidationRule(
expression="base_damage > 0",
message="Base damage must be positive"
)
]
)
```
### Template Expressions
Use Jinja2 templates for powerful derived field logic:
```python
# Simple expression
"max_hp": "{{ level * 8 + constitution * 2 }}"
# Conditional logic
"damage_bonus": "{% if strength >= 15 %}{{ (strength - 10) // 2 }}{% else %}0{% endif %}"
# Complex calculations
"skill_modifier": "{{ (skill_level + attribute_bonus - 10) // 2 }}"
```
### Validation Rules
Add custom validation logic to ensure data integrity:
```python
ValidationRule(
expression="level >= 1 and level <= 100",
message="Character level must be between 1 and 100"
),
ValidationRule(
expression="hp > 0 or status == 'dead'",
message="Living characters must have positive HP"
)
```
## ๐ง API Reference
### Core Classes
#### ModelDefinition
```python
ModelDefinition(
id: str, # Unique model identifier
name: str, # Human-readable name
namespace: str = "default", # Namespace for organization and inheritance
description: str = "", # Model description
attributes: Dict[str, AttributeDefinition], # Field definitions
extends: List[str] = None, # Parent model IDs (resolved in namespace)
validations: List[ValidationRule] = None # Validation rules
)
```
#### AttributeDefinition
```python
AttributeDefinition(
type: str, # Data type (str, int, float, bool, list, dict)
required: bool = False, # Whether field is required
default: Any = None, # Default value
derived: str = None, # Template expression for derived fields
range: str = None, # Value range constraint (e.g., "1..100")
enum: List[Any] = None, # Allowed values
pattern: str = None, # Regex pattern for strings
description: str = "" # Field description
)
```
#### GrimoireModel
```python
class GrimoireModel(MutableMapping):
def __init__(
self,
model_definition: ModelDefinition,
data: Dict[str, Any] = None,
template_resolver: TemplateResolver = None,
derived_field_resolver: DerivedFieldResolver = None,
**kwargs
)
# Dict-like interface
def __getitem__(self, key: str) -> Any
def __setitem__(self, key: str, value: Any) -> None
def __delitem__(self, key: str) -> None
def __iter__(self) -> Iterator[str]
def __len__(self) -> int
def keys(), values(), items()
# Batch operations
def batch_update(self, updates: Dict[str, Any]) -> None
# Path operations (dot notation)
def get(self, path: str, default: Any = None) -> Any
def set(self, path: str, value: Any) -> None
def has(self, path: str) -> bool
def delete(self, path: str) -> None
```
### Factory Functions
#### create_model
```python
def create_model(
model_definition: ModelDefinition,
data: Dict[str, Any] = None,
template_resolver_type: str = "jinja2",
derived_field_resolver_type: str = "batched",
**kwargs
) -> GrimoireModel
```
Creates a model instance with default resolvers. Inheritance is automatically resolved from the global model registry using namespaces.
### Global Registry Functions
```python
from grimoire_model import register_model, get_model, clear_registry
# Register model manually (usually automatic)
register_model("my_namespace", "my_model", model_definition)
# Retrieve model from registry
model_def = get_model("my_namespace", "my_model")
# Clear all models (useful for testing)
clear_registry()
# Access registry directly for advanced operations
from grimoire_model import get_model_registry
registry = get_model_registry()
registry_dict = registry.get_registry_dict()
all_namespaces = registry.list_namespaces()
```
### Template Resolvers
- `Jinja2TemplateResolver`: Standard Jinja2 template syntax
- `ModelContextTemplateResolver`: Simple `$variable` substitution
- `CachingTemplateResolver`: Cached template compilation for performance
### Derived Field Resolvers
- `BatchedDerivedFieldResolver`: Batches updates for performance
- `DerivedFieldResolver`: Immediate update resolver
## ๐งช Development
### Setup
```bash
git clone https://github.com/wyrdbound/grimoire-model.git
cd grimoire-model
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -e ".[dev]"
```
### Running Tests
```bash
# Run all tests with coverage
/Users/justingaylor/src/grimoire-model/.venv/bin/python -m pytest --cov=grimoire_model --cov-report=term
# Run specific test file
/Users/justingaylor/src/grimoire-model/.venv/bin/python -m pytest tests/test_model.py
# Run with verbose output
/Users/justingaylor/src/grimoire-model/.venv/bin/python -m pytest -v
# Generate HTML coverage report
/Users/justingaylor/src/grimoire-model/.venv/bin/python -m pytest --cov=grimoire_model --cov-report=html
# Open htmlcov/index.html in browser
```
_Note: Use the virtual environment in the project root as specified in the development guidelines._
### Code Quality
```bash
# Install development dependencies
source .venv/bin/activate && pip install ruff mypy
# Linting and formatting
source .venv/bin/activate && ruff check .
source .venv/bin/activate && ruff format .
# Type checking
source .venv/bin/activate && mypy src/grimoire_model/
# Run all quality checks
source .venv/bin/activate && ruff check . && mypy src/grimoire_model/
```
### Running Examples
```bash
# Basic usage example
source .venv/bin/activate && python examples/01_basic_usage.py
# Advanced features and inheritance
source .venv/bin/activate && python examples/02_advanced_usage.py
# Inheritance and polymorphism
source .venv/bin/activate && python examples/03_inheritance_polymorphism.py
# Performance and integration testing
source .venv/bin/activate && python examples/04_performance_integration.py
```
## ๐ Requirements
- Python 3.8+
- pydantic >= 2.0.0
- pyrsistent >= 0.19.0
- jinja2 >= 3.1.0
- pyyaml >= 6.0
### Development Dependencies
- pytest >= 7.0.0
- pytest-cov >= 4.0.0
- pytest-mock >= 3.0.0
- hypothesis >= 6.0.0
- mypy >= 1.0.0
- ruff >= 0.1.0
## ๐ฏ Use Cases
Grimoire Model excels in scenarios requiring structured, validated data with complex relationships:
- **RPG Character Systems**: Stats, levels, equipment with derived values
- **Game Item Management**: Equipment, inventory, crafting systems
- **Rule Engine Data**: Complex game mechanics with interdependent calculations
- **Configuration Systems**: Hierarchical configs with inheritance and validation
- **Dynamic Content**: Template-based content generation with context awareness
## ๐๏ธ Architecture
The package follows clean architecture principles with clear separation of concerns:
- **Core Layer**: Model definitions, schemas, and the main GrimoireModel class
- **Resolver Layer**: Pluggable template and derived field resolution systems
- **Validation Layer**: Type checking, constraints, and custom validation rules
- **Utils Layer**: Inheritance resolution, path utilities, and helper functions
- **Integration Layer**: grimoire-context compatibility and factory functions
### Key Design Principles
1. **Dependency Injection**: All major components can be swapped via constructor injection
2. **Immutable Operations**: Uses pyrsistent for efficient immutable data structures
3. **Template-Driven**: Jinja2 templates provide powerful expression capabilities
4. **Performance-Focused**: Batch updates and lazy evaluation minimize overhead
5. **Type Safety**: Full type hints and Pydantic integration for runtime validation
6. **Explicit Errors**: Prefers explicit errors over fallbacks to maintain system stability
## ๐ Performance
Current benchmarks (86% test coverage, 184 tests passing):
- **Model Creation**: ~1ms for simple models, ~5ms for complex inheritance
- **Field Updates**: ~0.1ms for direct fields, ~2ms for derived field cascades
- **Batch Updates**: 50-80% faster than individual updates for multiple fields
- **Memory Usage**: ~50KB per model instance (excluding data)
- **Template Resolution**: Cached compilation provides 10x speed improvement
## ๐ Integration with grimoire-context
Seamless integration is automatically enabled when both packages are installed:
```python
from grimoire_model import create_model, ModelDefinition, AttributeDefinition
from grimoire_context import GrimoireContext
# Models work naturally in contexts
character = create_model(character_def, character_data)
context = GrimoireContext({'player': character})
# Context operations automatically handle model updates
updated_context = context.set_variable('player.level', 25)
updated_character = updated_context['player']
# Derived fields update automatically
print(updated_character['max_hp']) # Recalculated based on new level
```
## ๐จ Error Handling
The package provides a comprehensive exception hierarchy:
```python
from grimoire_model import (
GrimoireModelError, # Base exception
ModelValidationError, # Validation failures
TemplateResolutionError, # Template processing errors
InheritanceError, # Model inheritance issues
DependencyError, # Derived field dependency issues
ConfigurationError # Setup and configuration errors
)
try:
character = create_model(character_def, invalid_data)
except ModelValidationError as e:
print(f"Validation failed: {e}")
print(f"Field: {e.field_name}")
print(f"Value: {e.field_value}")
print(f"Validation rule: {e.validation_rule}")
```
## ๐ Advanced Features
### Custom Template Resolvers
```python
from grimoire_model.resolvers.template import TemplateResolver
class CustomTemplateResolver(TemplateResolver):
def resolve_template(self, template: str, context: dict) -> str:
# Custom template logic
return processed_template
# Use custom resolver
model = GrimoireModel(
model_def,
data,
template_resolver=CustomTemplateResolver()
)
```
### Custom Validators
```python
from grimoire_model.validation.validators import ValidationEngine
def custom_validator(value, rule_params):
# Custom validation logic
return is_valid, error_message
# Register custom validator
engine = ValidationEngine()
engine.register_validator("custom_rule", custom_validator)
```
### Multiple Inheritance
```python
# Multiple parent models (all in same namespace)
combat_def = ModelDefinition(
id="character",
namespace="game", # All parent models must be in same namespace
extends=["base_entity", "combatant", "spell_caster"],
attributes={...}
)
# Automatic conflict resolution with left-to-right precedence
# Parents automatically resolved from "game" namespace
```
## ๐ License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for complete terms and conditions.
## ๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
If you have questions about the project, please contact: wyrdbound@proton.me
---
**Copyright (c) 2025 The Wyrd One**
Raw data
{
"_id": null,
"home_page": null,
"name": "grimoire-model",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "gaming, rpg, tabletop, model, validation, schema",
"author": null,
"author_email": "The Wyrd One <wyrdbound@proton.me>",
"download_url": "https://files.pythonhosted.org/packages/77/d6/ae7edc7acd027aef39a88ed1560115067d0e96f071696ca8f50a599953c9/grimoire_model-0.2.0.tar.gz",
"platform": null,
"description": "# Grimoire Model\n\n[](https://github.com/wyrdbound/grimoire-model/actions)\n[](https://www.python.org/downloads/)\n[](LICENSE)\n[](htmlcov/index.html)\n\n**Dict-like model system with schema validation, derived fields, and inheritance for the Grimoire tabletop RPG engine.**\n\nGrimoire Model provides a sophisticated, schema-driven model system that combines the familiar dict-like interface with powerful features like automatic field derivation, template-based expressions, model inheritance, and comprehensive validation. Designed to integrate seamlessly with `grimoire-context` for complete game state management.\n\n## \u2728 Features\n\n- **\ud83d\udcda Dict-like Interface**: Familiar Python dictionary operations with schema validation\n- **\ud83d\udd04 Reactive Derived Fields**: Automatic computation with dependency tracking and batch updates\n- **\ud83e\uddec Model Inheritance**: Multiple inheritance support with automatic namespace-based resolution\n- **\ud83d\udcdd Template Expressions**: Jinja2-powered field templates for dynamic content\n- **\ud83c\udff7\ufe0f Namespace Organization**: Global model registry with namespace-based organization\n- **\ud83d\udee1\ufe0f Schema Validation**: Pydantic-based type checking and custom validation rules\n- **\ud83d\udd27 Dependency Injection**: Pluggable resolvers for extensibility\n- **\u26a1 Performance Optimized**: Efficient batch updates and lazy evaluation\n- **\ud83c\udfaf grimoire-context Integration**: Seamless interoperability with context management\n\n## \ud83d\ude80 Quick Start\n\n### Installation\n\n```bash\npip install grimoire-model\n```\n\n### Basic Usage\n\n```python\nfrom grimoire_model import ModelDefinition, AttributeDefinition, create_model\n\n# Define a character model schema\ncharacter_def = ModelDefinition(\n id=\"character\",\n name=\"Player Character\",\n namespace=\"rpg\", # Organize models in namespaces\n attributes={\n \"name\": AttributeDefinition(type=\"str\", required=True),\n \"level\": AttributeDefinition(type=\"int\", default=1),\n \"hp\": AttributeDefinition(type=\"int\", default=100),\n \"mp\": AttributeDefinition(type=\"int\", default=50),\n\n # Derived fields automatically update when dependencies change\n \"max_hp\": AttributeDefinition(\n type=\"int\",\n derived=\"{{ level * 8 + hp }}\"\n ),\n \"character_summary\": AttributeDefinition(\n type=\"str\",\n derived=\"Level {{ level }} {{ name }} ({{ max_hp }} HP, {{ mp }} MP)\"\n )\n }\n)\n\n# Create a character instance\ncharacter = create_model(character_def, {\n \"name\": \"Aragorn\",\n \"level\": 15,\n \"hp\": 120,\n \"mp\": 80\n})\n\n# Dict-like interface with automatic derived field updates\nprint(character['name']) # \"Aragorn\"\nprint(character['max_hp']) # 240 (15 * 8 + 120)\nprint(character['character_summary']) # \"Level 15 Aragorn (240 HP, 80 MP)\"\n\n# Updates trigger automatic recalculation\ncharacter['level'] = 20\nprint(character['max_hp']) # 280 (20 * 8 + 120, automatically updated)\n```\n\n### Global Model Registry\n\nModels are automatically registered in a global registry using namespaces:\n\n```python\nfrom grimoire_model import get_model\n\n# Models auto-register when created\ncharacter_def = ModelDefinition(\n id=\"character\",\n namespace=\"rpg\", # Registered in \"rpg\" namespace\n # ... attributes ...\n)\n\n# Retrieve from anywhere in your application\nretrieved_def = get_model(\"rpg\", \"character\")\nnew_character = create_model(retrieved_def, {\"name\": \"Hero\"})\n\n# Perfect for inheritance - child models automatically find parents\nbase_def = ModelDefinition(id=\"base\", namespace=\"rpg\", ...)\nchild_def = ModelDefinition(id=\"child\", namespace=\"rpg\", extends=[\"base\"], ...)\n# No manual registry needed - inheritance resolves automatically!\n```\n\n### Model Inheritance with Namespaces\n\n```python\nfrom grimoire_model import get_model, clear_registry\n\n# Base entity definition (auto-registered in namespace)\nbase_entity_def = ModelDefinition(\n id=\"base_entity\",\n name=\"Base Entity\",\n namespace=\"game\", # Registered in \"game\" namespace\n attributes={\n \"id\": AttributeDefinition(type=\"str\", required=True),\n \"name\": AttributeDefinition(type=\"str\", required=True),\n \"description\": AttributeDefinition(type=\"str\", default=\"\")\n }\n)\n\n# Character extends base entity (automatic inheritance resolution)\ncharacter_def = ModelDefinition(\n id=\"character\",\n name=\"Character\",\n namespace=\"game\", # Same namespace enables automatic inheritance\n extends=[\"base_entity\"], # Automatically finds base_entity in namespace\n attributes={\n \"level\": AttributeDefinition(type=\"int\", default=1),\n \"hp\": AttributeDefinition(type=\"int\", default=100),\n \"max_hp\": AttributeDefinition(\n type=\"int\",\n derived=\"{{ level * 8 + hp }}\"\n )\n }\n)\n\n# Create character with inherited fields (no registry needed!)\ncharacter = create_model(\n character_def,\n {\n \"id\": \"char_001\", # From base_entity\n \"name\": \"Legolas\", # From base_entity\n \"description\": \"Elf archer\", # From base_entity\n \"level\": 12, # From character\n \"hp\": 96 # From character\n }\n)\n\nprint(character['id']) # \"char_001\" (inherited)\nprint(character['name']) # \"Legolas\" (inherited)\nprint(character['max_hp']) # 192 (derived field)\n\n# Retrieve models from global registry\nretrieved_char_def = get_model(\"game\", \"character\")\nanother_character = create_model(retrieved_char_def, {\n \"id\": \"char_002\",\n \"name\": \"Gimli\"\n})\n```\n\n### Integration with grimoire-context\n\n```python\nfrom grimoire_context import GrimoireContext\n\n# Create context with character model\ncontext = GrimoireContext({\n 'party': {\n 'leader': character,\n 'members': 4\n }\n})\n\n# Modify character through context - derived fields update automatically\ncontext = context.set_variable('party.leader.level', 25)\nupdated_character = context.get_variable('party.leader')\n\nprint(updated_character['level']) # 25\nprint(updated_character['max_hp']) # 296 (automatically recalculated)\n```\n\n### Batch Updates for Performance\n\n```python\n# Batch multiple changes for better performance\ncharacter.batch_update({\n 'level': 30,\n 'hp': 150,\n 'mp': 120\n})\n\n# All derived fields updated once after batch completion\nprint(character['max_hp']) # 390 (30 * 8 + 150)\n```\n\n## \ud83d\udcda Documentation\n\n- **[Logging Configuration](LOGGING.md)** - Configure library logging output and integration\n\n## \ud83d\udcda Core Concepts\n\n### Model Definitions\n\nModel definitions are schemas that describe the structure, types, and behavior of your data:\n\n```python\nmodel_def = ModelDefinition(\n id=\"weapon\",\n name=\"Weapon\",\n namespace=\"combat\", # Organize in combat namespace\n description=\"Combat weapon with damage calculations\",\n attributes={\n \"name\": AttributeDefinition(type=\"str\", required=True),\n \"base_damage\": AttributeDefinition(type=\"int\", default=1, range=\"1..50\"),\n \"enhancement\": AttributeDefinition(type=\"int\", default=0, range=\"0..10\"),\n\n # Derived field with complex logic\n \"total_damage\": AttributeDefinition(\n type=\"int\",\n derived=\"{{ base_damage + enhancement * 2 }}\"\n ),\n \"damage_category\": AttributeDefinition(\n type=\"str\",\n derived=\"{% if total_damage >= 20 %}High{% elif total_damage >= 10 %}Medium{% else %}Low{% endif %}\"\n )\n },\n validations=[\n ValidationRule(\n expression=\"base_damage > 0\",\n message=\"Base damage must be positive\"\n )\n ]\n)\n```\n\n### Template Expressions\n\nUse Jinja2 templates for powerful derived field logic:\n\n```python\n# Simple expression\n\"max_hp\": \"{{ level * 8 + constitution * 2 }}\"\n\n# Conditional logic\n\"damage_bonus\": \"{% if strength >= 15 %}{{ (strength - 10) // 2 }}{% else %}0{% endif %}\"\n\n# Complex calculations\n\"skill_modifier\": \"{{ (skill_level + attribute_bonus - 10) // 2 }}\"\n```\n\n### Validation Rules\n\nAdd custom validation logic to ensure data integrity:\n\n```python\nValidationRule(\n expression=\"level >= 1 and level <= 100\",\n message=\"Character level must be between 1 and 100\"\n),\nValidationRule(\n expression=\"hp > 0 or status == 'dead'\",\n message=\"Living characters must have positive HP\"\n)\n```\n\n## \ud83d\udd27 API Reference\n\n### Core Classes\n\n#### ModelDefinition\n\n```python\nModelDefinition(\n id: str, # Unique model identifier\n name: str, # Human-readable name\n namespace: str = \"default\", # Namespace for organization and inheritance\n description: str = \"\", # Model description\n attributes: Dict[str, AttributeDefinition], # Field definitions\n extends: List[str] = None, # Parent model IDs (resolved in namespace)\n validations: List[ValidationRule] = None # Validation rules\n)\n```\n\n#### AttributeDefinition\n\n```python\nAttributeDefinition(\n type: str, # Data type (str, int, float, bool, list, dict)\n required: bool = False, # Whether field is required\n default: Any = None, # Default value\n derived: str = None, # Template expression for derived fields\n range: str = None, # Value range constraint (e.g., \"1..100\")\n enum: List[Any] = None, # Allowed values\n pattern: str = None, # Regex pattern for strings\n description: str = \"\" # Field description\n)\n```\n\n#### GrimoireModel\n\n```python\nclass GrimoireModel(MutableMapping):\n def __init__(\n self,\n model_definition: ModelDefinition,\n data: Dict[str, Any] = None,\n template_resolver: TemplateResolver = None,\n derived_field_resolver: DerivedFieldResolver = None,\n **kwargs\n )\n\n # Dict-like interface\n def __getitem__(self, key: str) -> Any\n def __setitem__(self, key: str, value: Any) -> None\n def __delitem__(self, key: str) -> None\n def __iter__(self) -> Iterator[str]\n def __len__(self) -> int\n def keys(), values(), items()\n\n # Batch operations\n def batch_update(self, updates: Dict[str, Any]) -> None\n\n # Path operations (dot notation)\n def get(self, path: str, default: Any = None) -> Any\n def set(self, path: str, value: Any) -> None\n def has(self, path: str) -> bool\n def delete(self, path: str) -> None\n```\n\n### Factory Functions\n\n#### create_model\n\n```python\ndef create_model(\n model_definition: ModelDefinition,\n data: Dict[str, Any] = None,\n template_resolver_type: str = \"jinja2\",\n derived_field_resolver_type: str = \"batched\",\n **kwargs\n) -> GrimoireModel\n```\n\nCreates a model instance with default resolvers. Inheritance is automatically resolved from the global model registry using namespaces.\n\n### Global Registry Functions\n\n```python\nfrom grimoire_model import register_model, get_model, clear_registry\n\n# Register model manually (usually automatic)\nregister_model(\"my_namespace\", \"my_model\", model_definition)\n\n# Retrieve model from registry\nmodel_def = get_model(\"my_namespace\", \"my_model\")\n\n# Clear all models (useful for testing)\nclear_registry()\n\n# Access registry directly for advanced operations\nfrom grimoire_model import get_model_registry\nregistry = get_model_registry()\nregistry_dict = registry.get_registry_dict()\nall_namespaces = registry.list_namespaces()\n```\n\n### Template Resolvers\n\n- `Jinja2TemplateResolver`: Standard Jinja2 template syntax\n- `ModelContextTemplateResolver`: Simple `$variable` substitution\n- `CachingTemplateResolver`: Cached template compilation for performance\n\n### Derived Field Resolvers\n\n- `BatchedDerivedFieldResolver`: Batches updates for performance\n- `DerivedFieldResolver`: Immediate update resolver\n\n## \ud83e\uddea Development\n\n### Setup\n\n```bash\ngit clone https://github.com/wyrdbound/grimoire-model.git\ncd grimoire-model\npython -m venv .venv\nsource .venv/bin/activate # On Windows: .venv\\Scripts\\activate\npip install -e \".[dev]\"\n```\n\n### Running Tests\n\n```bash\n# Run all tests with coverage\n/Users/justingaylor/src/grimoire-model/.venv/bin/python -m pytest --cov=grimoire_model --cov-report=term\n\n# Run specific test file\n/Users/justingaylor/src/grimoire-model/.venv/bin/python -m pytest tests/test_model.py\n\n# Run with verbose output\n/Users/justingaylor/src/grimoire-model/.venv/bin/python -m pytest -v\n\n# Generate HTML coverage report\n/Users/justingaylor/src/grimoire-model/.venv/bin/python -m pytest --cov=grimoire_model --cov-report=html\n# Open htmlcov/index.html in browser\n```\n\n_Note: Use the virtual environment in the project root as specified in the development guidelines._\n\n### Code Quality\n\n```bash\n# Install development dependencies\nsource .venv/bin/activate && pip install ruff mypy\n\n# Linting and formatting\nsource .venv/bin/activate && ruff check .\nsource .venv/bin/activate && ruff format .\n\n# Type checking\nsource .venv/bin/activate && mypy src/grimoire_model/\n\n# Run all quality checks\nsource .venv/bin/activate && ruff check . && mypy src/grimoire_model/\n```\n\n### Running Examples\n\n```bash\n# Basic usage example\nsource .venv/bin/activate && python examples/01_basic_usage.py\n\n# Advanced features and inheritance\nsource .venv/bin/activate && python examples/02_advanced_usage.py\n\n# Inheritance and polymorphism\nsource .venv/bin/activate && python examples/03_inheritance_polymorphism.py\n\n# Performance and integration testing\nsource .venv/bin/activate && python examples/04_performance_integration.py\n```\n\n## \ud83d\udccb Requirements\n\n- Python 3.8+\n- pydantic >= 2.0.0\n- pyrsistent >= 0.19.0\n- jinja2 >= 3.1.0\n- pyyaml >= 6.0\n\n### Development Dependencies\n\n- pytest >= 7.0.0\n- pytest-cov >= 4.0.0\n- pytest-mock >= 3.0.0\n- hypothesis >= 6.0.0\n- mypy >= 1.0.0\n- ruff >= 0.1.0\n\n## \ud83c\udfaf Use Cases\n\nGrimoire Model excels in scenarios requiring structured, validated data with complex relationships:\n\n- **RPG Character Systems**: Stats, levels, equipment with derived values\n- **Game Item Management**: Equipment, inventory, crafting systems\n- **Rule Engine Data**: Complex game mechanics with interdependent calculations\n- **Configuration Systems**: Hierarchical configs with inheritance and validation\n- **Dynamic Content**: Template-based content generation with context awareness\n\n## \ud83c\udfd7\ufe0f Architecture\n\nThe package follows clean architecture principles with clear separation of concerns:\n\n- **Core Layer**: Model definitions, schemas, and the main GrimoireModel class\n- **Resolver Layer**: Pluggable template and derived field resolution systems\n- **Validation Layer**: Type checking, constraints, and custom validation rules\n- **Utils Layer**: Inheritance resolution, path utilities, and helper functions\n- **Integration Layer**: grimoire-context compatibility and factory functions\n\n### Key Design Principles\n\n1. **Dependency Injection**: All major components can be swapped via constructor injection\n2. **Immutable Operations**: Uses pyrsistent for efficient immutable data structures\n3. **Template-Driven**: Jinja2 templates provide powerful expression capabilities\n4. **Performance-Focused**: Batch updates and lazy evaluation minimize overhead\n5. **Type Safety**: Full type hints and Pydantic integration for runtime validation\n6. **Explicit Errors**: Prefers explicit errors over fallbacks to maintain system stability\n\n## \ud83d\udcc8 Performance\n\nCurrent benchmarks (86% test coverage, 184 tests passing):\n\n- **Model Creation**: ~1ms for simple models, ~5ms for complex inheritance\n- **Field Updates**: ~0.1ms for direct fields, ~2ms for derived field cascades\n- **Batch Updates**: 50-80% faster than individual updates for multiple fields\n- **Memory Usage**: ~50KB per model instance (excluding data)\n- **Template Resolution**: Cached compilation provides 10x speed improvement\n\n## \ud83d\udd04 Integration with grimoire-context\n\nSeamless integration is automatically enabled when both packages are installed:\n\n```python\nfrom grimoire_model import create_model, ModelDefinition, AttributeDefinition\nfrom grimoire_context import GrimoireContext\n\n# Models work naturally in contexts\ncharacter = create_model(character_def, character_data)\ncontext = GrimoireContext({'player': character})\n\n# Context operations automatically handle model updates\nupdated_context = context.set_variable('player.level', 25)\nupdated_character = updated_context['player']\n\n# Derived fields update automatically\nprint(updated_character['max_hp']) # Recalculated based on new level\n```\n\n## \ud83d\udea8 Error Handling\n\nThe package provides a comprehensive exception hierarchy:\n\n```python\nfrom grimoire_model import (\n GrimoireModelError, # Base exception\n ModelValidationError, # Validation failures\n TemplateResolutionError, # Template processing errors\n InheritanceError, # Model inheritance issues\n DependencyError, # Derived field dependency issues\n ConfigurationError # Setup and configuration errors\n)\n\ntry:\n character = create_model(character_def, invalid_data)\nexcept ModelValidationError as e:\n print(f\"Validation failed: {e}\")\n print(f\"Field: {e.field_name}\")\n print(f\"Value: {e.field_value}\")\n print(f\"Validation rule: {e.validation_rule}\")\n```\n\n## \ud83d\udd0d Advanced Features\n\n### Custom Template Resolvers\n\n```python\nfrom grimoire_model.resolvers.template import TemplateResolver\n\nclass CustomTemplateResolver(TemplateResolver):\n def resolve_template(self, template: str, context: dict) -> str:\n # Custom template logic\n return processed_template\n\n# Use custom resolver\nmodel = GrimoireModel(\n model_def,\n data,\n template_resolver=CustomTemplateResolver()\n)\n```\n\n### Custom Validators\n\n```python\nfrom grimoire_model.validation.validators import ValidationEngine\n\ndef custom_validator(value, rule_params):\n # Custom validation logic\n return is_valid, error_message\n\n# Register custom validator\nengine = ValidationEngine()\nengine.register_validator(\"custom_rule\", custom_validator)\n```\n\n### Multiple Inheritance\n\n```python\n# Multiple parent models (all in same namespace)\ncombat_def = ModelDefinition(\n id=\"character\",\n namespace=\"game\", # All parent models must be in same namespace\n extends=[\"base_entity\", \"combatant\", \"spell_caster\"],\n attributes={...}\n)\n\n# Automatic conflict resolution with left-to-right precedence\n# Parents automatically resolved from \"game\" namespace\n```\n\n## \ud83d\udcc4 License\n\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for complete terms and conditions.\n\n## \ud83e\udd1d Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\nIf you have questions about the project, please contact: wyrdbound@proton.me\n\n---\n\n**Copyright (c) 2025 The Wyrd One**\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Dict-like model system with schema validation, derived fields, and inheritance for Grimoire",
"version": "0.2.0",
"project_urls": {
"Homepage": "https://github.com/wyrdbound/grimoire-model",
"Issues": "https://github.com/wyrdbound/grimoire-model/issues",
"Repository": "https://github.com/wyrdbound/grimoire-model"
},
"split_keywords": [
"gaming",
" rpg",
" tabletop",
" model",
" validation",
" schema"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f0b3b40de1251bfb92f671e2fa721be1fa9b369fcd8f4f66335c88354f503a18",
"md5": "251195ea51c07b116ee4935ee60c8528",
"sha256": "316c17a321cd7e19a04f91dbc29f8956e9ee0fe2681209ed1e212ec40927cf48"
},
"downloads": -1,
"filename": "grimoire_model-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "251195ea51c07b116ee4935ee60c8528",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 41882,
"upload_time": "2025-09-12T07:29:54",
"upload_time_iso_8601": "2025-09-12T07:29:54.819732Z",
"url": "https://files.pythonhosted.org/packages/f0/b3/b40de1251bfb92f671e2fa721be1fa9b369fcd8f4f66335c88354f503a18/grimoire_model-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "77d6ae7edc7acd027aef39a88ed1560115067d0e96f071696ca8f50a599953c9",
"md5": "64744abd9619a28a7cb34b94b404aeb8",
"sha256": "fdd940c6e891991833336a52d951f343b8146a2bdc285bcba44d4af8dba69560"
},
"downloads": -1,
"filename": "grimoire_model-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "64744abd9619a28a7cb34b94b404aeb8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 67231,
"upload_time": "2025-09-12T07:29:56",
"upload_time_iso_8601": "2025-09-12T07:29:56.149400Z",
"url": "https://files.pythonhosted.org/packages/77/d6/ae7edc7acd027aef39a88ed1560115067d0e96f071696ca8f50a599953c9/grimoire_model-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-12 07:29:56",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "wyrdbound",
"github_project": "grimoire-model",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "grimoire-model"
}