Skip to main content

A code generator for dependency injection (DI) in Python which is based on the mediator and factory patterns

Project description

Reactor DI

CI PyPI version Coverage Status Python versions License: MIT

A code generator for dependency injection (DI) in Python based on the mediator and factory patterns.

Features

  • Two powerful decorators: @module and @law_of_demeter
  • Code generation approach: Generates DI code rather than runtime injection
  • Mediator pattern: Central coordination of dependencies
  • Factory pattern: Object creation abstraction
  • Type-safe: Full type hint support
  • Python 3.8+ support: Requires Python 3.8+ due to @cached_property usage

Installation

pip install reactor-di

Quick Start

from reactor_di import module, law_of_demeter, CachingStrategy

# Example: Database service with configuration forwarding
class DatabaseConfig:
    host = "localhost"
    port = 5432
    timeout = 30

@law_of_demeter("_config")
class DatabaseService:
    _config: DatabaseConfig
    _host: str      # Forwarded from config.host
    _port: int      # Forwarded from config.port
    _timeout: int   # Forwarded from config.timeout
    
    def connect(self) -> str:
        return f"Connected to {self._host}:{self._port} (timeout: {self._timeout}s)"

# Module: Automatic dependency injection with caching
@module(CachingStrategy.NOT_THREAD_SAFE)
class AppModule:
    config: DatabaseConfig      # Directly instantiated
    database: DatabaseService   # Synthesized with dependencies

# Usage
app = AppModule()
db_service = app.database
print(db_service.connect())  # → "Connected to localhost:5432 (timeout: 30s)"

# Properties are cleanly forwarded
print(db_service._host)      # → "localhost" (from config.host)
print(db_service._timeout)   # → 30 (from config.timeout)

Architecture

Reactor DI uses a modular file structure for clean separation of concerns:

  • module.py - The @module decorator for dependency injection containers
  • law_of_demeter.py - The @law_of_demeter decorator for property forwarding
  • caching.py - Caching strategies (CachingStrategy.DISABLED, CachingStrategy.NOT_THREAD_SAFE)
  • type_utils.py - Shared type checking utilities used across decorators

The decorators work together seamlessly - @law_of_demeter creates forwarding properties that @module recognizes during dependency validation, enabling clean cooperation without special configuration.

Advanced Usage

Caching Strategies

from reactor_di import module, CachingStrategy

# No caching - components created fresh each time
@module(CachingStrategy.DISABLED)
class DevModule:
    service: MyService

# Cached components - same instance returned (not thread-safe)
@module(CachingStrategy.NOT_THREAD_SAFE)
class ProdModule:
    service: MyService

Multiple Decorator Integration

@law_of_demeter("_config")    # Creates forwarding properties
@law_of_demeter("_module")    # Auto-setup: self._config = self._module.config
class ResourceController:
    def __init__(self, module):
        self._module = module
        # Decorator automatically sets up: self._config = module.config
    
    # From _config
    _timeout: int
    _is_dry_run: bool
    
    # From _module  
    _api: object
    _namespace: str

Custom Prefixes

# No prefix - direct forwarding
@law_of_demeter('config', prefix='')
class DirectController:
    timeout: int        # → config.timeout
    is_dry_run: bool    # → config.is_dry_run

# Custom prefix
@law_of_demeter('config', prefix='cfg_')
class PrefixController:
    cfg_timeout: int        # → config.timeout
    cfg_is_dry_run: bool    # → config.is_dry_run

API Reference

Core Decorators

@module(strategy: CachingStrategy = CachingStrategy.DISABLED)

Creates a dependency injection module that automatically instantiates and provides dependencies.

Parameters:

  • strategy: Caching strategy for component instances
    • CachingStrategy.DISABLED: Create new instances each time (default)
    • CachingStrategy.NOT_THREAD_SAFE: Cache instances (not thread-safe)

Usage:

@module(CachingStrategy.NOT_THREAD_SAFE)
class AppModule:
    config: Config
    service: Service  # Automatically injected with dependencies

@law_of_demeter(base_ref: str, prefix: str = "_")

Creates property forwarding from a base reference to avoid Law of Demeter violations.

Parameters:

  • base_ref: Name of the base object attribute to forward from
  • prefix: Prefix for forwarded property names (default: "_")

Usage:

@law_of_demeter("_config")
class Service:
    _timeout: int     # Forwards to _config.timeout
    _host: str        # Forwards to _config.host

Type Utilities

is_type_compatible(provided_type: Any, required_type: Any) -> bool

Checks if a provided type is compatible with a required type for dependency injection.

Logic:

  1. Exact type match: provided_type == required_type
  2. String annotations: Name-based comparison
  3. None types: Must both be None
  4. Class inheritance: issubclass(provided_type, required_type)
  5. Complex types: Conservative fallback to True

safe_get_type_hints(cls: Type[Any]) -> Dict[str, Any]

Safely retrieves type hints with fallback to __annotations__ on error.

get_all_type_hints(cls: Type[Any]) -> Dict[str, Any]

Collects type hints from entire inheritance hierarchy (MRO).

needs_implementation(cls: Type[Any], attr_name: str) -> bool

Determines if an attribute needs implementation by checking:

  • Has annotation but no implementation
  • Is an abstract method/property in inheritance hierarchy

Enums

CachingStrategy

Component caching strategies for the @module decorator.

  • DISABLED = "disabled": No caching, create new instances each time
  • NOT_THREAD_SAFE = "not_thread_safe": Cache instances (not thread-safe)

Development

This project uses modern Python tooling and best practices:

Setup

  1. Clone the repository
  2. Install dependencies:
    uv sync --all-groups
    

Running Tests

# Run all tests (126 tests, 100% coverage)
uv run pytest

# Run tests with coverage reporting
uv run pytest --cov=reactor_di

# Run specific test modules
uv run pytest tests/test_module.py          # Module decorator tests
uv run pytest tests/test_law_of_demeter.py  # Law of Demeter decorator tests  
uv run pytest tests/test_type_utils.py      # Type compatibility utilities tests
uv run pytest tests/test_integration.py     # Integration tests between decorators

Code Quality

# Run linting
uv run ruff check src tests
uv run black --check src tests

# Run type checking
uv run mypy src

# Fix formatting
uv run black src tests

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality with meaningful assertions
  5. Ensure all tests pass and 100% coverage is maintained
  6. Verify realistic code scenarios rather than mocking impossible edge cases
  7. Submit a pull request

License

This project is licensed under the MIT License - see the 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

reactor_di-0.1.0.tar.gz (81.6 kB view details)

Uploaded Source

Built Distribution

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

reactor_di-0.1.0-py3-none-any.whl (15.9 kB view details)

Uploaded Python 3

File details

Details for the file reactor_di-0.1.0.tar.gz.

File metadata

  • Download URL: reactor_di-0.1.0.tar.gz
  • Upload date:
  • Size: 81.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.7.21

File hashes

Hashes for reactor_di-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0dd1ead7aa5e3cc1a1d596d320ca42c6a9cf516f8872abd5726687619995f2f6
MD5 e0fd4f7c3d70744b38b09657519b40a3
BLAKE2b-256 6d8d451c19e4fec561df20f73e5860a3e454cd83635280e066d18da2e55471e4

See more details on using hashes here.

File details

Details for the file reactor_di-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: reactor_di-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.7.21

File hashes

Hashes for reactor_di-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7e4212c5033e4779eae263375ddeb6c2861061561a7a8e37faa3b21448b00fa0
MD5 c5ed6cc4f7e3f6032d9f05c87eea2e01
BLAKE2b-256 51710a1ec65985eb77def56f416a7b112309af17280b6fa2acc5401f202d4c17

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