A minimalist, zero-dependency Inversion of Control (IoC) container for Python.
Project description
Pico-IoC: A Minimalist IoC Container for Python
Pico-IoC is a tiny, zero-dependency, decorator-based Inversion of Control (IoC) container for Python.
It helps you manage dependencies in a clean, intuitive, and Pythonic way.
The core idea is to let you build loosely coupled, easily testable applications without manually wiring components.
Inspired by the IoC philosophy popularized by the Spring Framework.
Key Features
- ✨ Zero Dependencies: Pure Python, no external libraries.
- 🚀 Decorator-Based API: Simple decorators like
@componentand@provides. - 🔍 Automatic Discovery: Scans your package to auto-register components.
- 🧩 Lazy Instantiation: Objects are created on first use.
- 🏭 Flexible Factories: Encapsulate complex creation logic.
- 🤝 Framework-Agnostic: Works with Flask, FastAPI, CLIs, scripts, etc.
Installation
pip install pico-ioc
Quick Start
Getting started is simple. Decorate your classes and let Pico-IoC wire them up.
from pico_ioc import component, init
@component
class AppConfig:
def get_db_url(self):
return "postgresql://user:pass@host/db"
@component
class DatabaseService:
def __init__(self, config: AppConfig):
self._cs = config.get_db_url()
def get_data(self):
return f"Data from {self._cs}"
# Initialize the container scanning the current module
container = init(__name__)
db = container.get(DatabaseService)
print(db.get_data()) # Data from postgresql://user:pass@host/db
More Examples
🧩 Custom Component Name
from pico_ioc import component, init
@component(name="config")
class AppConfig:
def __init__(self):
self.db_url = "postgresql://user:pass@localhost/db"
@component
class Repository:
def __init__(self, config: "config"): # refer by custom name if you prefer
self._url = config.db_url
container = init(__name__)
repo = container.get(Repository)
print(repo._url) # postgresql://user:pass@localhost/db
print(container.get("config").db_url)
Pico-IoC prefers type annotations to resolve deps; if missing, it falls back to the parameter name.
💤 Lazy Factories (only build when used)
from pico_ioc import factory_component, provides, init
CREATION_COUNTER = {"value": 0}
@factory_component
class ServicesFactory:
@provides(name="heavy_service") # returns a LazyProxy by default
def make_heavy(self):
CREATION_COUNTER["value"] += 1
return {"payload": "Hello from heavy service"}
container = init(__name__)
svc = container.get("heavy_service")
print(CREATION_COUNTER["value"]) # 0 (not created yet)
print(svc["payload"]) # triggers creation
print(CREATION_COUNTER["value"]) # 1
📦 Project-Style Package Scanning
project_root/
└── myapp/
├── __init__.py
├── services.py
└── main.py
myapp/services.py
from pico_ioc import component
@component
class Config:
def __init__(self):
self.base_url = "https://api.example.com"
@component
class ApiClient:
def __init__(self, config: Config):
self.base_url = config.base_url
def get(self, path: str):
return f"GET {self.base_url}/{path}"
myapp/main.py
import pico_ioc
from myapp.services import ApiClient
# Scan the whole 'myapp' package
container = pico_ioc.init("myapp")
client = container.get(ApiClient)
print(client.get("status")) # GET https://api.example.com/status
API Reference (mini)
init(root_package_or_module) -> PicoContainer
Initialize the container by scanning a root package (str) or module. Returns the configured container.
@component(cls=None, *, name: str | None = None)
Mark a class as a component. Registered by class type by default, or by name if provided.
@factory_component
Mark a class as a factory holder. Methods inside can be @provides(...).
@provides(name: str, lazy: bool = True)
Declare that a factory method produces a component registered under name. By default it’s lazy (a proxy that creates on first real use).
Testing
pico-ioc ships with pytest tests and a tox matrix.
pip install tox
tox -e py311 # run on a specific version
tox # run all configured envs
Contributing
Issues and PRs are welcome. If you spot a bug or have an idea, open an issue!
License
MIT — see the LICENSE.
Authors
- David Perez Cabrera
- Gemini 2.5-Pro
- GPT-5
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-0.1.1.tar.gz.
File metadata
- Download URL: pico_ioc-0.1.1.tar.gz
- Upload date:
- Size: 9.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fdfee600ccb20d53e54ac091083a3716931dbfa192d56ea5af61f6f7cfaa2eb4
|
|
| MD5 |
2a828a3758a1c2207007031b01104d22
|
|
| BLAKE2b-256 |
665631ef2274658c836d1c34453e3e369c6fd3041e183a8f2d895ca1b2b09584
|
Provenance
The following attestation bundles were made for pico_ioc-0.1.1.tar.gz:
Publisher:
publish-to-pypi.yml on dperezcabrera/pico-ioc
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pico_ioc-0.1.1.tar.gz -
Subject digest:
fdfee600ccb20d53e54ac091083a3716931dbfa192d56ea5af61f6f7cfaa2eb4 - Sigstore transparency entry: 369233789
- Sigstore integration time:
-
Permalink:
dperezcabrera/pico-ioc@654edb45b170b790087853303350621651dd0941 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/dperezcabrera
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@654edb45b170b790087853303350621651dd0941 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pico_ioc-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pico_ioc-0.1.1-py3-none-any.whl
- Upload date:
- Size: 6.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbf9f1f0638c054c2428f5392ed7cb561a6ed153b81de598467158c51230aef3
|
|
| MD5 |
87b19d6feafca7a0c84ebe63ef382e67
|
|
| BLAKE2b-256 |
3c99e4fb90a2701b8a785220d7bf4fcba9b7d5fc6dd9bfecc54766503e52b3ba
|
Provenance
The following attestation bundles were made for pico_ioc-0.1.1-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on dperezcabrera/pico-ioc
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pico_ioc-0.1.1-py3-none-any.whl -
Subject digest:
fbf9f1f0638c054c2428f5392ed7cb561a6ed153b81de598467158c51230aef3 - Sigstore transparency entry: 369233822
- Sigstore integration time:
-
Permalink:
dperezcabrera/pico-ioc@654edb45b170b790087853303350621651dd0941 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/dperezcabrera
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@654edb45b170b790087853303350621651dd0941 -
Trigger Event:
release
-
Statement type: