Elegant Python Dependency Injection with Profile-Aware Configuration
Bindry is a powerful yet intuitive dependency injection framework for Python that supports profile-based configuration, environment variable interpolation, and flexible component lifecycle management. It enables you to write more maintainable and testable applications by managing dependencies through configuration rather than hard-coding them.
As a Spring developer, I was eager to explore the possibilities of dependency injection in Python. While Spring's robust ecosystem has been instrumental in shaping my understanding of modern software development, I found myself craving a framework that could leverage Python's unique strengths and flexibility. Bindry fills this gap by providing an elegant and intuitive solution for managing dependencies in Python applications.
- ποΈ Profile-based configuration management
- π Environment variable support with fallback values
- π YAML and JSON configuration file support
- βοΈ Constructor, method, and property injection
- 𧩠Singleton and prototype scoping
- π Type-based automatic dependency resolution
- π οΈ Support for constructor arguments via configuration
- π·οΈ Flexible component registration through decorators or configuration files
pip install bindry
from bindry import component, Scope, autowired
# Define an interface
class MessageService:
def send_message(self, msg: str): pass
# Implement the service
# Register the bean in application-context.yaml
class EmailService(MessageService):
def send_message(self, msg: str):
print(f"Sending email: {msg}")
# Use declarator to register the bean
@component(scope=Scope.SINGLETON)
class NotificationManager:
# MessageService will be injected
def __init__(self, message_service: MessageService):
self.message_service = message_service
def notify(self, message: str):
self.message_service.send_message(message)
Create a application-context.yaml
file:
profiles:
default:
beans:
MessageService:
bean_type: "myapp.services.MessageService"
implementation: "myapp.services.EmailService"
scope: "singleton"
constructor_args:
timeout: 30
development:
beans:
MessageService:
bean_type: "myapp.services.MessageService"
implementation: "myapp.services.MockMessageService"
scope: "singleton"
from bindry import ApplicationContext
# Initialize the context
context = ApplicationContext.get_instance()
context.load_configuration("application-context.yaml", active_profiles=["development"])
# Get and use components
notification_manager = context.get_bean(NotificationManager)
notification_manager.notify("Hello, World!")
Bindry supports environment variable interpolation in configuration files:
profiles:
default:
beans:
DatabaseService:
bean_type: "myapp.services.DatabaseService"
implementation: "myapp.services.DatabaseService"
scope: "singleton"
constructor_args:
url: "${DATABASE_URL:sqlite:///default.db}"
timeout: "${DB_TIMEOUT:30}"
context = ApplicationContext.get_instance()
context.set_active_profiles(["default", "development", "testing"])
Set the ACTIVE_PROFILES
environment variable:
export ACTIVE_PROFILES=development,testing
@component(
scope=Scope.SINGLETON,
bean_type=DatabaseService,
constructor_args={
"timeout": 30,
"retries": 3,
"kwargs": {"debug": True}
}
)
class DatabaseService:
def __init__(self, timeout: int, retries: int, **kwargs):
self.timeout = timeout
self.retries = retries
self.debug = kwargs.get("debug", False)
@component(Scope.SINGLETON)
class ServiceManager:
@autowired
def configure(self, config_service: ConfigService):
self.config_service = config_service
We welcome contributions! Please see our Contributing Guide for details.
This project is licensed under the MIT License - see the LICENSE file for details.
This project was developed with the assistance of AI language models:
- Initial implementation assistance provided by ChatGPT
- Additional feature development and refinements by Claude.ai
While the code was primarily generated through AI assistance, all implementations have been carefully reviewed and tested to ensure quality and reliability.