grimoire-model


Namegrimoire-model JSON
Version 0.2.0 PyPI version JSON
download
home_pageNone
SummaryDict-like model system with schema validation, derived fields, and inheritance for Grimoire
upload_time2025-09-12 07:29:56
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
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

[![Tests](https://github.com/wyrdbound/grimoire-model/workflows/Tests/badge.svg)](https://github.com/wyrdbound/grimoire-model/actions)
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Coverage](https://img.shields.io/badge/coverage-88%25-green.svg)](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[![Tests](https://github.com/wyrdbound/grimoire-model/workflows/Tests/badge.svg)](https://github.com/wyrdbound/grimoire-model/actions)\n[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![Coverage](https://img.shields.io/badge/coverage-88%25-green.svg)](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"
}
        
Elapsed time: 4.82385s