Skip to main content

Model Mirror

Project description

ModelMirror

A Python library for automatic configuration management using JSON files. It lets you describe object instances and their dependencies in JSON, then automatically creates and connects those objects for you.

Key Features

  • Non-Intrusive: Works with existing classes without modification
  • Simple Registration: Just create a registry entry linking schema to class
  • JSON Configuration: Human-readable configuration files
  • Automatic Dependency Injection: Reference instances with $name syntax
  • Singleton Management: Reuse instances across your configuration
  • Type Safety: Optional Pydantic integration for type checking
  • Dependency Resolution: Automatic topological sorting of dependencies

Tutorial 1: Quick Start - Your First Working Example

Let's create a simple example with two classes: a DatabaseService and a UserService that depends on it.

Step 1: Define Your Classes (No Changes Needed!)

# Your existing classes - no modifications required
class DatabaseService:
    def __init__(self, host: str, port: int):
        self.host = host
        self.port = port

    def connect(self):
        return f"Connected to {self.host}:{self.port}"

class UserService:
    def __init__(self, db: DatabaseService, cache_enabled: bool):
        self.db = db
        self.cache_enabled = cache_enabled

    def get_user(self, user_id: int):
        connection = self.db.connect()
        return f"User {user_id} from {connection} (cache: {self.cache_enabled})"

Step 2: Register Your Classes

Create registry entries that link your classes to schema identifiers:

from modelmirror.class_provider.class_register import ClassRegister
from modelmirror.class_provider.class_reference import ClassReference

# Register DatabaseService with schema "database" and version "1.0.0"
class DatabaseServiceRegister(ClassRegister,
    reference=ClassReference(schema="database", version="1.0.0", cls=DatabaseService)):
    pass

# Register UserService with schema "user_service" and version "1.0.0"
class UserServiceRegister(ClassRegister,
    reference=ClassReference(schema="user_service", version="1.0.0", cls=UserService)):
    pass

Step 3: Create JSON Configuration

Create a config.json file that defines your instances:

{
    "my_database": {
        "$reference": {
            "registry": {"schema": "database", "version": "1.0.0"},
            "instance": "db_singleton"
        },
        "host": "localhost",
        "port": 5432
    },
    "my_user_service": {
        "$reference": {
            "registry": {"schema": "user_service", "version": "1.0.0"}
        },
        "db": "$db_singleton",
        "cache_enabled": true
    }
}

Step 4: Load and Use

from modelmirror.mirror import Mirror

# Load configuration
mirror = Mirror('myapp')  # 'myapp' is the package where your registers are defined
instances = mirror.reflect_raw('config.json')

# Get your configured instances
user_service = instances.get(UserService)
print(user_service.get_user(123))  # Output: User 123 from Connected to localhost:5432 (cache: True)

That's it! Your classes are now configured via JSON with automatic dependency injection.

Understanding the Key Concepts

The $reference Object

Every instance in your JSON config needs a $reference object with two main parts:

1. registry - Links to Your Python Class

"registry": {"schema": "database", "version": "1.0.0"}
  • Purpose: Tells ModelMirror which Python class to instantiate
  • Must match: The schema and version in your ClassReference registration
  • Required: Always needed to identify the class

2. instance - Creates a Singleton Reference (Optional)

"instance": "db_singleton"
  • Purpose: Creates a named singleton that can be referenced elsewhere
  • Usage: Reference it with "$db_singleton" in other instances
  • Optional: Only needed if you want to reuse this instance

Dependency Injection with $ References

Use $instance_name to inject dependencies:

{
    "database": {
        "$reference": {
            "registry": {"schema": "database", "version": "1.0.0"},
            "instance": "main_db"
        },
        "host": "localhost",
        "port": 5432
    },
    "user_service": {
        "$reference": {
            "registry": {"schema": "user_service", "version": "1.0.0"}
        },
        "db": "$main_db",
        "cache_enabled": true
    },
    "admin_service": {
        "$reference": {
            "registry": {"schema": "admin_service", "version": "1.0.0"}
        },
        "db": "$main_db",
        "timeout": 30
    }
}

In this example:

  • database creates a singleton named main_db
  • Both user_service and admin_service inject the same main_db instance
  • ModelMirror automatically resolves dependencies in the correct order

Tutorial 2: Type-Safe Configuration with Pydantic Schema

For production applications, you want compile-time type checking and IDE support. ModelMirror integrates with Pydantic to provide a type-safe configuration schema.

Step 1: Define Your Classes (Same as Tutorial 1)

# Your existing classes - no modifications required
class DatabaseService:
    def __init__(self, host: str, port: int):
        self.host = host
        self.port = port

    def connect(self):
        return f"Connected to {self.host}:{self.port}"

class UserService:
    def __init__(self, db: DatabaseService, cache_enabled: bool):
        self.db = db
        self.cache_enabled = cache_enabled

    def get_user(self, user_id: int):
        connection = self.db.connect()
        return f"User {user_id} from {connection} (cache: {self.cache_enabled})"

Step 2: Register Your Classes (Same as Tutorial 1)

from modelmirror.class_provider.class_register import ClassRegister
from modelmirror.class_provider.class_reference import ClassReference

# Register DatabaseService with schema "database" and version "1.0.0"
class DatabaseServiceRegister(ClassRegister,
    reference=ClassReference(schema="database", version="1.0.0", cls=DatabaseService)):
    pass

# Register UserService with schema "user_service" and version "1.0.0"
class UserServiceRegister(ClassRegister,
    reference=ClassReference(schema="user_service", version="1.0.0", cls=UserService)):
    pass

Step 3: Create JSON Configuration (Same as Tutorial 1)

{
    "my_database": {
        "$reference": {
            "registry": {"schema": "database", "version": "1.0.0"},
            "instance": "db_singleton"
        },
        "host": "localhost",
        "port": 5432
    },
    "my_user_service": {
        "$reference": {
            "registry": {"schema": "user_service", "version": "1.0.0"}
        },
        "db": "$db_singleton",
        "cache_enabled": true
    }
}

Step 4: Define Your Pydantic Schema

Create a Pydantic model that describes your configuration structure:

from pydantic import BaseModel, ConfigDict

class AppConfig(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    my_database: DatabaseService
    my_user_service: UserService

Step 5: Load with Type Validation

from modelmirror.mirror import Mirror

# Load configuration with full type checking
mirror = Mirror('myapp')
config = mirror.reflect_typed('config.json', AppConfig)

# Now you have full IDE support and type safety
print(config.my_database.host)  # IDE autocomplete works!
print(config.my_user_service.get_user(456))  # Type-safe method calls

That's it! Your classes are now configured via JSON with automatic dependency injection AND full type safety.

Benefits of Using a Schema

  • IDE Support: Full autocomplete and IntelliSense
  • Type Checking: Catch configuration errors at load time
  • Documentation: Schema serves as living documentation
  • Validation: Pydantic validates all field types automatically
  • Refactoring Safety: IDE can track usage across your codebase

Example with Validation

from pydantic import BaseModel, Field
from typing import List

class DatabaseConfig(BaseModel):
    host: str = Field(..., min_length=1)
    port: int = Field(..., ge=1, le=65535)
    max_connections: int = Field(default=10, ge=1)

class AppConfig(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    databases: List[DatabaseService]
    user_service: UserService
    debug_mode: bool = False

# This will validate all constraints when loading
config = mirror.reflect_typed('config.json', AppConfig)

Advanced Usage

Working with Lists

{
    "services": [
        {
            "$reference": {
                "registry": {"schema": "service", "version": "1.0.0"},
                "instance": "service_1"
            },
            "name": "Primary Service"
        },
        "$service_1",
        {
            "$reference": {
                "registry": {"schema": "service", "version": "1.0.0"}
            },
            "name": "Secondary Service"
        }
    ]
}

Retrieving Instances

# Get single instance by type
user_service = instances.get(UserService)

# Get instance by singleton name
database = instances.get(DatabaseService, '$main_db')

# Get all instances of a type as list
all_services = instances.get(list[ServiceClass])

# Get all instances as dictionary
service_dict = instances.get(dict[str, ServiceClass])

Installation

pip install modelmirror

Requirements

  • Python >= 3.10
  • Pydantic >= 2.0.0

License

MIT License - see LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

modelmirror-0.1.10.tar.gz (64.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

modelmirror-0.1.10-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

File details

Details for the file modelmirror-0.1.10.tar.gz.

File metadata

  • Download URL: modelmirror-0.1.10.tar.gz
  • Upload date:
  • Size: 64.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for modelmirror-0.1.10.tar.gz
Algorithm Hash digest
SHA256 1141aeabaea270dc0dcb5de08e7f3ac6aaf6529e977ed78fc837bbc2408f7848
MD5 5d3697ad6a8ef94521f28204b66ae711
BLAKE2b-256 5ffb1f3fecdd6f7430478b118fff29ff41d5d435696917060ed2a72e42dafdaa

See more details on using hashes here.

File details

Details for the file modelmirror-0.1.10-py3-none-any.whl.

File metadata

  • Download URL: modelmirror-0.1.10-py3-none-any.whl
  • Upload date:
  • Size: 10.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for modelmirror-0.1.10-py3-none-any.whl
Algorithm Hash digest
SHA256 3599f660e04283173cee13ad7d3d746bd7813cd88ddc64d5c836fb22178695aa
MD5 694e904d93965758a2009f79cf54d362
BLAKE2b-256 fa777d149deb1ac5a3c6e764b49002b392e08cb89abc2f5b0e8d40a76c440a84

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page