A code generator for dependency injection (DI) in Python which is based on the mediator and factory patterns
Project description
Reactor DI
A code generator for dependency injection (DI) in Python based on the mediator and factory patterns.
Features
- Two powerful decorators:
@moduleand@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_propertyusage
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@moduledecorator for dependency injection containerslaw_of_demeter.py- The@law_of_demeterdecorator for property forwardingcaching.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 instancesCachingStrategy.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 fromprefix: 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:
- Exact type match:
provided_type == required_type - String annotations: Name-based comparison
- None types: Must both be None
- Class inheritance:
issubclass(provided_type, required_type) - 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 timeNOT_THREAD_SAFE = "not_thread_safe": Cache instances (not thread-safe)
Development
This project uses modern Python tooling and best practices:
Setup
- Clone the repository
- 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
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality with meaningful assertions
- Ensure all tests pass and 100% coverage is maintained
- Verify realistic code scenarios rather than mocking impossible edge cases
- Submit a pull request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0dd1ead7aa5e3cc1a1d596d320ca42c6a9cf516f8872abd5726687619995f2f6
|
|
| MD5 |
e0fd4f7c3d70744b38b09657519b40a3
|
|
| BLAKE2b-256 |
6d8d451c19e4fec561df20f73e5860a3e454cd83635280e066d18da2e55471e4
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e4212c5033e4779eae263375ddeb6c2861061561a7a8e37faa3b21448b00fa0
|
|
| MD5 |
c5ed6cc4f7e3f6032d9f05c87eea2e01
|
|
| BLAKE2b-256 |
51710a1ec65985eb77def56f416a7b112309af17280b6fa2acc5401f202d4c17
|