A lightweight, Pythonic dependency injection container inspired by Symfony's DependencyInjection component. This library embraces Python's philosophy of simplicity while providing powerful tools for organizing complex applications.
Dependency injection isn't just enterprise complexity - it's a Pythonic pattern that promotes:
- Clear separation of concerns - Each class has a single responsibility
- Testability - Easy to mock dependencies for unit tests
- Flexibility - Change implementations without touching existing code
- Maintainability - Explicit dependencies make code easier to understand
This approach aligns perfectly with Python's zen: "Explicit is better than implicit" and "Readability counts".
pip install ioc
- Define your services in a
services.ymlfile:
parameters: database_url: "sqlite:///app.db" debug_mode: true services: # Database connection database: class: myapp.database.Database arguments: ["%database_url%"] # User repository with injected database user_repository: class: myapp.repositories.UserRepository arguments: ["@database"] # User service with injected repository user_service: class: myapp.services.UserService arguments: ["@user_repository"] calls: - [set_debug, ["%debug_mode%"]]
- Use the container in your application:
import ioc # Build container from configuration container = ioc.build(['services.yml']) # Get your services - dependencies are automatically resolved! user_service = container.get('user_service') # Your service is ready to use with all dependencies injected users = user_service.get_all_users()
This library follows Python best practices:
- Configuration over code - Define dependencies in YAML, not scattered across your codebase
- Explicit dependencies - See exactly what each service needs at a glance
- No magic - Simple, predictable behavior that follows Python conventions
- Framework agnostic - Works with Flask, Django, FastAPI, or pure Python
services: # Constructor injection email_service: class: myapp.EmailService arguments: ["@mailer", "%sender_email%"] # Method calls after construction logger: class: logging.Logger arguments: ["myapp"] calls: - [setLevel, ["INFO"]] - [addHandler, ["@file_handler"]] # Weak references (lazy loading) cache_service: class: myapp.CacheService arguments: ["#@redis_client"] # Only loaded when needed
parameters: # String interpolation log_file: "/var/log/%app_name%.log" # Environment variables secret_key: "%env(SECRET_KEY)%" # Default values redis_url: "%env(REDIS_URL):redis://localhost:6379%"
With dependency injection, testing becomes straightforward:
import unittest from unittest.mock import Mock class TestUserService(unittest.TestCase): def test_create_user(self): # Mock the repository mock_repo = Mock() mock_repo.save.return_value = True # Inject the mock user_service = UserService(mock_repo) # Test with confidence result = user_service.create_user("john@example.com") self.assertTrue(result) mock_repo.save.assert_called_once()
Contributions are welcome! This project follows Python community standards:
- PEP 8 code style
- Type hints for better IDE support
- Comprehensive tests
- Clear documentation
Licensed under the Apache License 2.0. See LICENSE for details.
"Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex." - The Zen of Python
This library embodies these principles while providing the power and flexibility needed for serious Python applications.