A minimalist, zero-dependency Inversion of Control (IoC) container for Python.
Project description
📦 Pico-IoC: A Robust, Async-Native IoC Container for Python
pico-ioc is a robust, async-native, decorator-based IoC container for Python. It helps you build loosely-coupled, testable, enterprise-grade applications without manual wiring. Inspired by the Spring ecosystem, but fully Pythonic.
⚠️ Requires Python 3.10+ (due to extensive use of modern
typingfeatures).
⚖️ Principles
- Focus & Simplicity: A declarative API for one job: managing dependencies. It avoids accidental complexity by doing one thing well.
- Declarative & Explicit: No magic. Behavior is deterministic, relying on explicit decorators (
@component,@factory) and type hints. - Unified Composition Root: The application is assembled from a single entry point (
init) which defines a clear, predictable boundary. - Fail-Fast by Design: Catches circular dependencies and missing bindings at startup, not at runtime. If the application runs, it's wired correctly.
- Testability First: Features like
@conditional, profiles, andoverridesare first-class citizens, enabling fast and isolated testing. - Async Native & Extensible: Full
async/awaitsupport, AOP (@intercepted_by), and a built-inEventBusare available out-of-the-box. - Framework Agnostic: Zero hard dependencies (standard library only). It works with any Python application, from simple scripts to complex web servers.
✨ Why Pico-IoC?
pico-ioc exists to solve a common problem that arises as Python applications grow: managing how objects are created and connected becomes complex and brittle. This manual wiring, where a change deep in the application can cause a cascade of updates, makes the code hard to test and maintain.
pico-ioc introduces the principle of Inversion of Control (IoC) in a simple, Pythonic way. Instead of you creating and connecting every object, you declare your components with a simple @component decorator, and the container automatically wires them together based on their type hints. It brings the architectural robustness and testability of mature frameworks like Spring to the Python ecosystem, allowing you to build complex, loosely-coupled applications that remain simple to manage.
| Feature | Manual Wiring | With Pico-IoC |
|---|---|---|
| Object Creation | service = Service(Repo(Config())) |
svc = container.get(Service) |
| Testing | Manual replacement or monkey-patching | overrides={Repo: FakeRepo()} |
| Coupling | High (code knows about constructors) | Low (code just asks for a type) |
| Maintenance | Brittle (changing a constructor breaks consumers) | Robust (changes are isolated) |
| Learning Curve | Ad-hoc, implicit patterns | Uniform, explicit, documented |
🧩 Features
Core
- Zero external dependencies — pure Python, framework-agnostic.
- Decorator-based API —
@component,@factory,@provides,@configuration. - Fail-fast Bootstrap — Detects circular dependencies and missing bindings at startup.
- Async-Native Resolution — Full
async/awaitsupport withcontainer.aget(). - Sophisticated Scopes —
singleton,prototype, andContextVar-based scopes (e.g.,request,session). - Typed Configuration — Injects
dataclassesfrom environment/files via@configuration. - Test-Driven — Built-in
overridesandprofilesfor easy mocking.
Advanced
- AOP / Interceptors — Intercept method calls with
@intercepted_by. - Qualifiers — Inject subsets of components with
Annotated[List[T], Qualifier(...)]. - Async Event Bus — Built-in
EventBusfor decoupled, event-driven architecture. - Conditional Registration —
@conditional(by profile, env var) and@on_missing(fallbacks). - Lifecycle Hooks —
@configure(post-init) and@cleanup(on shutdown). - Health Checks — Built-in
@healthdecorator andcontainer.health_check(). - Serializable Proxies — Lazy (
@lazy) and AOP proxies arepickle-safe.
📦 Installation
# Requires Python 3.10+
pip install pico-ioc
🚀 Quick Start
from pico_ioc import component, init, configuration
from dataclasses import dataclass
@configuration
@dataclass
class Config:
url: str = "sqlite:///demo.db"
@component
class Repo:
def __init__(self, cfg: Config):
self.url = cfg.url
def fetch(self):
return f"fetching from {self.url}"
@component
class Service:
def __init__(self, repo: Repo):
self.repo = repo
def run(self):
return self.repo.fetch()
# Bootstrap the container by scanning modules
# We use __name__ to scan the current module
container = init(modules=[__name__])
# Resolve the service and run
svc = container.get(Service)
print(svc.run())
Output:
fetching from sqlite:///demo.db
Quick Overrides for Testing
The init function accepts overrides to replace any component for testing.
import my_app_module
from pico_ioc import init
# Define a fake repository
class FakeRepo:
def fetch(self):
return "fake-data"
# Initialize the container, overriding the real Repo
container = init(
modules=[my_app_module],
overrides={
Repo: FakeRepo() # Override by type
}
)
# The service now receives FakeRepo instead of the real one
svc = container.get(Service)
assert svc.run() == "fake-data"
📖 Documentation
-
🚀 New to pico-ioc? Start with the User Guide.
- guide.md — Learn with practical examples: testing, configuration, AOP, async, and web framework integration.
-
🏗️ Want to understand the internals? See the Architecture.
- architecture.md — A deep dive into the resolution algorithm, lifecycle, and internal design.
🧪 Development
pip install tox
tox
📜 Changelog
See CHANGELOG.md for version history.
📜 License
MIT — see LICENSE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pico_ioc-2.0.0.tar.gz.
File metadata
- Download URL: pico_ioc-2.0.0.tar.gz
- Upload date:
- Size: 135.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ccfdcc0209f4a7405616176d6f2aa9b7f2097c088bdcd681f43c91fbd47dc4da
|
|
| MD5 |
f3bc94768101b76213761a7efa42c2bc
|
|
| BLAKE2b-256 |
ea55eec84f064763be83567801ed7855adbe77efeb9d993b3185fba0bff16dce
|
File details
Details for the file pico_ioc-2.0.0-py3-none-any.whl.
File metadata
- Download URL: pico_ioc-2.0.0-py3-none-any.whl
- Upload date:
- Size: 29.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f8adc91c7c0279045dc7790e61a2d18e3d342f81c9ef048df8a433ec6423e90
|
|
| MD5 |
0053c49e5abe46479cb955b0d2ca427a
|
|
| BLAKE2b-256 |
385618f984d75ef4ed759baa676e954ff4bdb91d3dd01ea51f1a866d1de54618
|