implement-dependency-injection
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWorks with container.py and container_factory.py files.
适用于container.py和container_factory.py文件。
Implement Dependency Injection
实现依赖注入
Purpose
用途
Guides proper integration of services into the dependency injection Container using the library. Ensures correct provider selection, prevents circular dependencies, and enables testability through override patterns.
dependency-injector指导如何使用库将服务正确集成到依赖注入容器中。确保选择正确的提供者类型、避免循环依赖,并通过重载模式提升可测试性。
dependency-injectorWhen 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"
在以下场景使用本技能:
- 创建新服务 - 添加需要依赖项的服务
- 向容器添加依赖 - 编排组件以实现注入
- 调试循环依赖错误 - 解决依赖循环问题
- 编排组件以实现注入 - 搭建正确的依赖流转
- 设置测试重载 - 在测试中模拟依赖项
触发短语:
- "将X添加到依赖容器"
- "为Y服务编排依赖"
- "修复循环依赖错误"
- "为测试重载依赖"
Quick Start
快速开始
Adding a simple service to Container:
python
undefined向容器添加简单服务:
python
undefinedIn container.py
In container.py
my_service = providers.Singleton(
MyService,
settings=settings,
dependency1=some_dependency,
)
undefinedmy_service = providers.Singleton(
MyService,
settings=settings,
dependency1=some_dependency,
)
undefinedTable 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
- 用途 - 技能的单句目标
- 快速开始 - 可直接复制使用的示例
- 操作步骤 - 完整的实现指南
- 步骤1:确定提供者类型 - Singleton vs Factory vs Dependency
- 步骤2:向容器添加服务 - 集成到Container.py
- 步骤3:处理特殊情况 - 工厂函数、配置提取、循环依赖
- 步骤4:测试重载 - 测试夹具模式
- 步骤5:在工厂中初始化 - MCP服务器初始化
- 示例 - 具体的可运行实现
- 要求 - 依赖项、文件、质量检查
- 常见陷阱 - 故障排除指南
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
- references/provider-reference.md - 完整的dependency-injector API参考
- references/circular-dependency-guide.md - 循环依赖解决策略
- templates/service-template.py - 带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
- Detect Circular Dependencies - 检测DI容器中的循环依赖
- Generate Provider - 自动生成容器的提供者代码
- Validate DI Patterns - 验证代码库中的依赖注入模式
Instructions
操作步骤
Step 1: Determine Provider Type
步骤1:确定提供者类型
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
根据生命周期需求选择合适的提供者:
使用:
providers.Singleton- 有状态服务(仓库、缓存)
- 初始化成本高的服务(数据库连接、嵌入模型)
- 请求之间需要共享状态
- 大多数应用/基础设施服务
使用:
providers.Factory- 请求处理器(命令/查询处理器)
- 无状态操作
- 每次调用需要新实例
- 短生命周期对象
使用:
providers.Dependency- 运行时提供的外部依赖
- 配置对象(Settings)
- 基础设施连接(neo4j_driver)
- 容器外部的服务
Step 2: Add Service to Container
步骤2:向容器添加服务
Location:
/Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container.pyAdd provider in the appropriate layer section:
python
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.
位置:
/Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container.py在对应层级的区域添加提供者:
python
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,
)顺序很重要: 先定义依赖项,再定义依赖它的服务,避免循环引用。
Step 3: Handle Special Cases
步骤3:处理特殊情况
Factory Functions (for complex initialization):
python
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:
python
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 for resolution strategies
references/circular-dependency-guide.md
工厂函数(用于复杂初始化):
python
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,
)提取配置值:
python
smart_chunking_service = providers.Singleton(
SmartChunkingService,
boundary_adjustment_lines=providers.Factory(
lambda s: s.chunking.boundary_adjustment_lines,
s=settings,
),
settings=settings,
)避免循环依赖:
- 调整提供者顺序:先依赖项,后依赖它的服务
- 使用实现延迟加载
providers.Factory - 考虑将共享依赖提取到单独的提供者中
- 查看获取解决策略
references/circular-dependency-guide.md
Step 4: Override for Testing
步骤4:测试重载
In test files (conftest.py or test methods):
python
@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在测试文件中(conftest.py或测试方法):
python
@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 NoneStep 5: Initialize in Factory
步骤5:在工厂中初始化
For MCP server initialization:
Update :
/Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container_factory.pypython
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针对MCP服务器初始化:
更新 :
/Users/dawiddutoit/projects/play/project-watch-mcp/src/project_watch_mcp/interfaces/mcp/container_factory.pypython
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 containerExamples
示例
Example 1: Add Repository (Singleton)
示例1:添加仓库(Singleton)
python
undefinedpython
undefinedIn container.py - Infrastructure Layer section
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.file_metadata_repository = providers.Singleton(
FileMetadataRepository,
driver=neo4j_driver,
settings=settings,
)
**为什么用Singleton:** 仓库是有状态的,管理连接,创建成本高。Example 2: Add Command Handler (Factory)
示例2:添加命令处理器(Factory)
python
undefinedpython
undefinedIn container.py - Application Layer section
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.process_file_handler = providers.Factory(
ProcessFileHandler,
repository=file_metadata_repository,
chunking_service=chunking_service,
settings=settings,
)
**为什么用Factory:** 处理器按请求调用,应该是无状态的。Example 3: Override for Testing
示例3:测试重载
python
undefinedpython
undefinedIn tests/integration/mymodule/conftest.py
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 containerdef test_handler_uses_repository(container):
handler = container.process_file_handler()
# handler now uses mocked driver through repository
undefined@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 containerdef test_handler_uses_repository(container):
handler = container.process_file_handler()
# handler now uses mocked driver through repository
undefinedRequirements
要求
Dependencies
依赖项
bash
uv pip install dependency-injectorbash
uv pip install dependency-injectorProject 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
- 容器定义:
src/project_watch_mcp/interfaces/mcp/container.py - 工厂初始化:
src/project_watch_mcp/interfaces/mcp/container_factory.py - 测试示例:
tests/integration/container/test_settings_injection.py
Quality Checks
质量检查
After adding to container:
bash
undefined添加到容器后执行:
bash
undefinedType checking (ensures proper wiring)
类型检查(确保编排正确)
uv run pyright src/project_watch_mcp/interfaces/mcp/container.py
uv run pyright src/project_watch_mcp/interfaces/mcp/container.py
Run tests
运行测试
uv run pytest tests/integration/container/ -v
undefineduv run pytest tests/integration/container/ -v
undefinedCommon Pitfalls
常见陷阱
1. Circular Dependency Error
1. 循环依赖错误
Symptom:
Error while resolving dependenciesSolution: Reorder providers or use factory functions. See .
references/circular-dependency-guide.md症状:
Error while resolving dependencies解决方案: 调整提供者顺序或使用工厂函数。查看。
references/circular-dependency-guide.md2. Wrong Provider Type
2. 错误的提供者类型
Symptom: State leaking between tests or requests
Solution: Use Factory for handlers, Singleton for services/repositories.
症状: 测试或请求之间状态泄露
解决方案: 处理器使用Factory,服务/仓库使用Singleton。
3. Missing Override in Tests
3. 测试中缺少重载
Symptom: Tests fail with "Dependency not set"
Solution: Override all Dependency providers in test fixtures.
症状: 测试因"Dependency not set"失败
解决方案: 在测试夹具中重载所有Dependency提供者。
4. Optional Dependencies
4. 可选依赖
Symptom:
def __init__(self, settings: Settings | None = None)Solution: NEVER make Settings/Config optional. Use and override in tests.
providers.Dependency(instance_of=Settings)症状:
def __init__(self, settings: Settings | None = None)解决方案: 绝对不要将Settings/Config设为可选。使用并在测试中重载。
providers.Dependency(instance_of=Settings)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
- references/provider-reference.md - 完整的dependency-injector API参考
- references/circular-dependency-guide.md - 循环依赖解决策略
- templates/service-template.py - 带DI的服务类模板
- scripts/detect_circular_deps.py - 循环依赖检测工具
- scripts/generate_provider.py - 提供者代码生成工具
- scripts/validate_di_patterns.py - DI模式验证工具