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: trueservices:
# Database connectiondatabase:
class: myapp.database.Databasearguments: ["%database_url%"]# User repository with injected databaseuser_repository:
class: myapp.repositories.UserRepositoryarguments: ["@database"]# User service with injected repositoryuser_service:
class: myapp.services.UserServicearguments: ["@user_repository"]calls:
- [set_debug, ["%debug_mode%"]]- Use the container in your application:
importioc# Build container from configurationcontainer=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 injectedusers=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 injectionemail_service:
class: myapp.EmailServicearguments: ["@mailer", "%sender_email%"]# Method calls after constructionlogger:
class: logging.Loggerarguments: ["myapp"]calls:
- [setLevel, ["INFO"]]
- [addHandler, ["@file_handler"]]# Weak references (lazy loading)cache_service:
class: myapp.CacheServicearguments: ["#@redis_client"] # Only loaded when neededparameters:
# String interpolationlog_file: "/var/log/%app_name%.log"# Environment variablessecret_key: "%env(SECRET_KEY)%"# Default valuesredis_url: "%env(REDIS_URL):redis://localhost:6379%"With dependency injection, testing becomes straightforward:
importunittestfromunittest.mockimportMockclassTestUserService(unittest.TestCase):
deftest_create_user(self):
# Mock the repositorymock_repo=Mock()
mock_repo.save.return_value=True# Inject the mockuser_service=UserService(mock_repo)
# Test with confidenceresult=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.