Loading...
Loading...
Provides comprehensive guide for adding services to dependency injection Container using dependency-injector library patterns including Singleton vs Factory vs Dependency providers, override patterns for testing, and circular dependency detection. Use when creating new service, adding dependency to Container, debugging circular dependency errors, or wiring components for injection.
npx skill4agent add dawiddutoit/custom-claude implement-dependency-injectiondependency-injector# In container.py
my_service = providers.Singleton(
MyService,
settings=settings,
dependency1=some_dependency,
)providers.Singletonproviders.Factoryproviders.Dependency/Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container.pyclass Container(containers.DeclarativeContainer):
# Configuration
settings = providers.Dependency(instance_of=Settings)
# Infrastructure Layer - Repositories
my_repository = providers.Singleton(
MyRepository,
driver=neo4j_driver,
settings=settings,
)
# Application Layer - Services
my_service = providers.Singleton(
MyService,
settings=settings,
repository=my_repository,
)
# Application Layer - Command Handlers
my_handler = providers.Factory(
MyHandler,
service=my_service,
)def create_extractor_registry(python_ext):
"""Factory function to create and configure extractor registry."""
registry = ExtractorRegistry()
registry.register(python_ext)
return registry
extractor_registry = providers.Singleton(
create_extractor_registry,
python_ext=python_extractor,
)smart_chunking_service = providers.Singleton(
SmartChunkingService,
boundary_adjustment_lines=providers.Factory(
lambda s: s.chunking.boundary_adjustment_lines,
s=settings,
),
settings=settings,
)providers.Factoryreferences/circular-dependency-guide.md@pytest.fixture
def container(real_settings, mock_driver):
"""Create container with test overrides."""
container = Container()
# Override dependencies
container.settings.override(real_settings)
container.neo4j_driver.override(mock_driver)
return container
def test_service(container):
"""Test service from container."""
service = container.my_service()
assert service.settings is not None/Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container_factory.pydef initialize_container_and_services(
repository_monitor: RepositoryMonitor,
neo4j_rag: Neo4jRAG,
settings: Settings,
) -> Container:
"""Initialize container with runtime dependencies."""
container = Container()
# Override external dependencies
container.settings.override(settings)
container.neo4j_driver.override(neo4j_rag.neo4j_database.driver)
container.repository_monitor.override(repository_monitor)
# Initialize services with side effects
_ = container.my_service()
return container# In container.py - Infrastructure Layer section
file_metadata_repository = providers.Singleton(
FileMetadataRepository,
driver=neo4j_driver,
settings=settings,
)# In container.py - Application Layer section
process_file_handler = providers.Factory(
ProcessFileHandler,
repository=file_metadata_repository,
chunking_service=chunking_service,
settings=settings,
)# In tests/integration/mymodule/conftest.py
@pytest.fixture
def container(real_settings):
container = Container()
container.settings.override(real_settings)
# Override with mock driver
mock_driver = AsyncMock()
container.neo4j_driver.override(mock_driver)
return container
def test_handler_uses_repository(container):
handler = container.process_file_handler()
# handler now uses mocked driver through repositoryuv pip install dependency-injectorsrc/project_watch_mcp/interfaces/mcp/container.pysrc/project_watch_mcp/interfaces/mcp/container_factory.pytests/integration/container/test_settings_injection.py# Type checking (ensures proper wiring)
uv run pyright src/project_watch_mcp/interfaces/mcp/container.py
# Run tests
uv run pytest tests/integration/container/ -vError while resolving dependenciesreferences/circular-dependency-guide.mddef __init__(self, settings: Settings | None = None)providers.Dependency(instance_of=Settings)