implement-dependency-injection
npx skills add https://github.com/dawiddutoit/custom-claude --skill implement-dependency-injection
Agent 安装分布
Skill 文档
Works with container.py and container_factory.py files.
Implement Dependency Injection
Purpose
Guides proper integration of services into the dependency injection Container using the dependency-injector library. Ensures correct provider selection, prevents circular dependencies, and enables testability through override patterns.
When to Use
Use this skill when:
- Creating new service – Adding a service that needs dependencies
- Adding dependency to Container – Wiring components for injection
- Debugging circular dependency errors – Resolving dependency cycles
- Wiring components for injection – Setting up proper dependency flow
- Setting up test overrides – Mocking dependencies in tests
Trigger phrases:
- “Add X to the dependency container”
- “Wire Y service with dependencies”
- “Fix circular dependency error”
- “Override dependency for testing”
Quick Start
Adding a simple service to Container:
# In container.py
my_service = providers.Singleton(
MyService,
settings=settings,
dependency1=some_dependency,
)
Table of Contents
Core Sections
- Purpose – Single-sentence skill objective
- Quick Start – Immediate, copy-paste ready example
- Instructions – Complete implementation guide
- Step 1: Determine Provider Type – Singleton vs Factory vs Dependency
- Step 2: Add Service to Container – Container.py integration
- Step 3: Handle Special Cases – Factory functions, config extraction, circular dependencies
- Step 4: Override for Testing – Test fixture patterns
- Step 5: Initialize in Factory – MCP server initialization
- Examples – Concrete, working implementations
- Requirements – Dependencies, files, quality checks
- Common Pitfalls – Troubleshooting guide
Supporting Resources
- references/provider-reference.md – Complete dependency-injector API reference
- references/circular-dependency-guide.md – Circular dependency resolution strategies
- templates/service-template.py – Service class template with DI
Utility Scripts
- Detect Circular Dependencies – Detect circular dependencies in DI container
- Generate Provider – Auto-generate provider code for container
- Validate DI Patterns – Validate dependency injection patterns in codebase
Instructions
Step 1: Determine Provider Type
Choose the appropriate provider based on lifecycle requirements:
Use providers.Singleton:
- Stateful services (repositories, caches)
- Expensive initialization (database connections, embedding models)
- Shared state needed across requests
- Most application/infrastructure services
Use providers.Factory:
- Request handlers (command/query handlers)
- Stateless operations
- New instance needed per invocation
- Short-lived objects
Use providers.Dependency:
- External dependencies provided at runtime
- Configuration objects (Settings)
- Infrastructure connections (neo4j_driver)
- Services from outside the container
Step 2: Add Service to Container
Location: /Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container.py
Add provider in the appropriate layer section:
class 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,
)
Ordering matters: Define dependencies before dependents to avoid circular references.
Step 3: Handle Special Cases
Factory Functions (for complex initialization):
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,
)
Extracting Config Values:
smart_chunking_service = providers.Singleton(
SmartChunkingService,
boundary_adjustment_lines=providers.Factory(
lambda s: s.chunking.boundary_adjustment_lines,
s=settings,
),
settings=settings,
)
Avoiding Circular Dependencies:
- Order providers: dependencies before dependents
- Use lazy evaluation with
providers.Factory - Consider extracting shared dependencies to separate provider
- See
references/circular-dependency-guide.mdfor resolution strategies
Step 4: Override for Testing
In test files (conftest.py or test methods):
@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
Step 5: Initialize in Factory
For MCP server initialization:
Update /Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container_factory.py:
def 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
Examples
Example 1: Add Repository (Singleton)
# In container.py - Infrastructure Layer section
file_metadata_repository = providers.Singleton(
FileMetadataRepository,
driver=neo4j_driver,
settings=settings,
)
Why Singleton: Repositories are stateful, manage connections, expensive to create.
Example 2: Add Command Handler (Factory)
# In container.py - Application Layer section
process_file_handler = providers.Factory(
ProcessFileHandler,
repository=file_metadata_repository,
chunking_service=chunking_service,
settings=settings,
)
Why Factory: Handlers are invoked per request, should be stateless.
Example 3: Override for Testing
# 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 repository
Requirements
Dependencies
uv pip install dependency-injector
Project Files
- Container definition:
src/project_watch_mcp/interfaces/mcp/container.py - Factory initialization:
src/project_watch_mcp/interfaces/mcp/container_factory.py - Test examples:
tests/integration/container/test_settings_injection.py
Quality Checks
After adding to container:
# Type checking (ensures proper wiring)
uv run pyright src/project_watch_mcp/interfaces/mcp/container.py
# Run tests
uv run pytest tests/integration/container/ -v
Common Pitfalls
1. Circular Dependency Error
Symptom: Error while resolving dependencies
Solution: Reorder providers or use factory functions. See references/circular-dependency-guide.md.
2. Wrong Provider Type
Symptom: State leaking between tests or requests
Solution: Use Factory for handlers, Singleton for services/repositories.
3. Missing Override in Tests
Symptom: Tests fail with “Dependency not set”
Solution: Override all Dependency providers in test fixtures.
4. Optional Dependencies
Symptom: def __init__(self, settings: Settings | None = None)
Solution: NEVER make Settings/Config optional. Use providers.Dependency(instance_of=Settings) and override in tests.
See Also
- references/provider-reference.md – Complete dependency-injector API reference
- references/circular-dependency-guide.md – Circular dependency resolution strategies
- templates/service-template.py – Service class template with DI
- scripts/detect_circular_deps.py – Detect circular dependencies utility
- scripts/generate_provider.py – Generate provider code utility
- scripts/validate_di_patterns.py – Validate DI patterns utility