Implementation of dependency injection for Python 3
Overall
score
92%
Convention-based service registration and resolution using string names and aliases instead of type annotations. This approach enables flexible service registration patterns and runtime service resolution by name.
Register a single name-to-type mapping for convention-based service resolution.
def add_alias(self, name: str, desired_type: Type) -> Container:
"""
Add a single alias mapping name to type.
Args:
name: String name/alias for the service
desired_type: Type to associate with the name
Returns:
Container instance for method chaining
Raises:
AliasAlreadyDefined: If alias already exists
"""Register multiple name-to-type mappings at once using a dictionary.
def add_aliases(self, values: AliasesTypeHint) -> Container:
"""
Add multiple alias mappings from a dictionary.
Args:
values: Dictionary mapping names to types
Returns:
Container instance for method chaining
Raises:
AliasAlreadyDefined: If any alias already exists
"""Set a single alias with optional override capability for existing aliases.
def set_alias(self, name: str, desired_type: Type, override: bool = False) -> Container:
"""
Set a single alias mapping, optionally overriding existing aliases.
Args:
name: String name/alias for the service
desired_type: Type to associate with the name
override: If True, allows overriding existing aliases
Returns:
Container instance for method chaining
Raises:
AliasAlreadyDefined: If alias exists and override is False
"""Set multiple aliases with optional override capability.
def set_aliases(self, values: AliasesTypeHint, override: bool = False) -> Container:
"""
Set multiple alias mappings, optionally overriding existing aliases.
Args:
values: Dictionary mapping names to types
override: If True, allows overriding existing aliases
Returns:
Container instance for method chaining
Raises:
AliasAlreadyDefined: If any alias exists and override is False
"""Type definition for alias dictionaries mapping string names to types.
AliasesTypeHint = Dict[str, Type]Utility function to convert class names to standard parameter names following Python conventions.
def to_standard_param_name(name) -> str:
"""
Convert class names to standard parameter names.
Args:
name: Class name to convert
Returns:
Converted parameter name (e.g., "UserService" -> "user_service")
"""Register services using naming conventions instead of explicit type mappings:
# Register services with conventional names
container.register(UserService)
container.register(DatabaseService)
container.register(EmailService)
# Add aliases for convention-based resolution
aliases = {
"user_service": UserService,
"database_service": DatabaseService,
"email_service": EmailService
}
container.add_aliases(aliases)
# Resolve by name
user_service = container.resolve("user_service")Use aliases to enable parameter name-based dependency injection:
def business_method(user_service, email_service, database_service):
# Method automatically gets services by parameter names
pass
# Services will be resolved by matching parameter names to aliases
services.exec(business_method)Use aliases for configuration-driven service registration:
# Configuration defines service mappings
service_config = {
"user_repository": "SqlUserRepository",
"email_provider": "SmtpEmailService",
"cache_provider": "RedisCache"
}
# Register services based on configuration
for alias, service_name in service_config.items():
service_type = globals()[service_name] # Get type by name
container.register(service_type)
container.add_alias(alias, service_type)Use aliases to switch between different implementations:
# Development environment
dev_aliases = {
"database": MockDatabase,
"email_service": ConsoleEmailService,
"storage": LocalFileStorage
}
# Production environment
prod_aliases = {
"database": PostgreSQLDatabase,
"email_service": SmtpEmailService,
"storage": S3Storage
}
# Register based on environment
if environment == "development":
container.set_aliases(dev_aliases, override=True)
else:
container.set_aliases(prod_aliases, override=True)Resolve services dynamically at runtime using string names:
def get_service_by_name(service_name: str):
"""Dynamically resolve service by name."""
return services.get(service_name)
# Usage
service_name = user_input # From user, config, etc.
service = get_service_by_name(service_name)Use aliases to support plugin-based architectures:
# Plugin registration
def register_plugin(plugin_name: str, plugin_class: Type):
container.register(plugin_class)
container.add_alias(f"plugin_{plugin_name}", plugin_class)
# Plugin loading
def load_plugin(plugin_name: str):
alias = f"plugin_{plugin_name}"
if alias in container:
return container.resolve(alias)
else:
raise ValueError(f"Plugin '{plugin_name}' not found")Alias-related exceptions that may be raised during alias operations:
class AliasAlreadyDefined(DIException):
"""Raised when attempting to add an alias that already exists."""
class AliasConfigurationError(DIException):
"""Raised when an alias references an unconfigured type."""These exceptions help identify configuration issues:
try:
container.add_alias("existing_alias", NewService)
except AliasAlreadyDefined:
# Handle duplicate alias
container.set_alias("existing_alias", NewService, override=True)
try:
service = container.resolve("unknown_alias")
except AliasConfigurationError:
# Handle missing alias configuration
logging.error("Service alias not configured")Install with Tessl CLI
npx tessl i tessl/pypi-rodidocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10