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.
- Smart Dependency Resolution: Resolves by parameter name, then type annotation, then MRO fallback.
- Auto-Exclude Caller:
init()automatically skips the calling module to avoid double-initialization during scans.
📦 Installation
pip install pico-ioc
🚀 Quick Start
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
🧩 Custom Component Keys
You can register a component with a custom key (string, class, enum…).
key= is the preferred syntax. For backwards compatibility, name= still works.
from pico_ioc import component, init
@component(name="config") # still supported for legacy code
class AppConfig:
def __init__(self):
self.db_url = "postgresql://user:pass@localhost/db"
@component
class Repository:
def __init__(self, config: "config"): # resolve by name
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)
🏭 Factory Components and @provides
Factories can provide components under a specific key.
Default is lazy creation (via LazyProxy).
from pico_ioc import factory_component, provides, init
CREATION_COUNTER = {"value": 0}
@factory_component
class ServicesFactory:
@provides(key="heavy_service") # preferred
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 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
container = pico_ioc.init("myapp")
client = container.get(ApiClient)
print(client.get("status")) # GET https://api.example.com/status
🧠 Dependency Resolution Order
When Pico-IoC instantiates a component, it tries to resolve each parameter in this order:
- Exact parameter name (string key in container)
- Exact type annotation (class key in container)
- MRO fallback (walk base classes until match)
- String version of the parameter name
🛠 API Reference
init(root_package_or_module, *, exclude=None, auto_exclude_caller=True) -> PicoContainer
Scan the given root package (str) or module. By default, excludes the calling module.
@component(cls=None, *, name=None)
Register a class as a component.
If name is given, registers under that string; otherwise under the class type.
@factory_component
Register a class as a factory of components.
@provides(key=None, *, name=None, lazy=True)
Declare that a factory method provides a component under key.
name is accepted for backwards compatibility.
If lazy=True, returns a LazyProxy that instantiates on first real use.
🧪 Testing
pip install tox
tox -e py311
📜 License
MIT — see LICENSE
¿Quieres que también te prepare un ejemplo completo en el README con fast_model y BaseChatModel para que quede documentado el nuevo orden de resolución? Así quedaría clarísimo para cualquiera que lo use.
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.2.1.tar.gz.
File metadata
- Download URL: pico_ioc-0.2.1.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ebd1ef3e5d5acb22beccbb16002ece3874713c69f1dc6f18b6df3f0e5feb24c
|
|
| MD5 |
186c7cb3110005274301186cccda9b4b
|
|
| BLAKE2b-256 |
b32e0731792649bd03029da01c0b706261fca212b3b680fde8ed41fce2fa0af0
|
Provenance
The following attestation bundles were made for pico_ioc-0.2.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.2.1.tar.gz -
Subject digest:
3ebd1ef3e5d5acb22beccbb16002ece3874713c69f1dc6f18b6df3f0e5feb24c - Sigstore transparency entry: 372023005
- Sigstore integration time:
-
Permalink:
dperezcabrera/pico-ioc@cc2a8619163d8598fe4a4d1cd2726d1998230363 -
Branch / Tag:
refs/tags/v0.2.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@cc2a8619163d8598fe4a4d1cd2726d1998230363 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pico_ioc-0.2.1-py3-none-any.whl.
File metadata
- Download URL: pico_ioc-0.2.1-py3-none-any.whl
- Upload date:
- Size: 7.3 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 |
16eda968134007e1291e244fd320d72f9fc6af0211584ec76fb845c0ab480b9a
|
|
| MD5 |
7b250fa9682518102e981de0d1f0f357
|
|
| BLAKE2b-256 |
19dd7e105453b609581df722d76efe88e14f80ca89454ed689cd4b3aa51c0be6
|
Provenance
The following attestation bundles were made for pico_ioc-0.2.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.2.1-py3-none-any.whl -
Subject digest:
16eda968134007e1291e244fd320d72f9fc6af0211584ec76fb845c0ab480b9a - Sigstore transparency entry: 372023020
- Sigstore integration time:
-
Permalink:
dperezcabrera/pico-ioc@cc2a8619163d8598fe4a4d1cd2726d1998230363 -
Branch / Tag:
refs/tags/v0.2.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@cc2a8619163d8598fe4a4d1cd2726d1998230363 -
Trigger Event:
release
-
Statement type: