Skip to main content

A minimalist, zero-dependency Inversion of Control (IoC) container for Python.

Project description

📦 Pico-IoC: A Robust, Async-Native IoC Container for Python

PyPI Ask DeepWiki License: MIT CI (tox matrix) codecov Quality Gate Status Duplicated Lines (%) Maintainability Rating

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 typing features).


⚖️ 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, and overrides are first-class citizens, enabling fast and isolated testing.
  • Async Native & Extensible: Full async/await support, AOP (@intercepted_by), and a built-in EventBus are 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/await support with container.aget().
  • Sophisticated Scopessingleton, prototype, and ContextVar-based scopes (e.g., request, session).
  • Typed Configuration — Injects dataclasses from environment/files via @configuration.
  • Test-Driven — Built-in overrides and profiles for 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 EventBus for 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 @health decorator and container.health_check().
  • Serializable Proxies — Lazy (@lazy) and AOP proxies are pickle-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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pico_ioc-2.0.0.tar.gz (135.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pico_ioc-2.0.0-py3-none-any.whl (29.5 kB view details)

Uploaded Python 3

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

Hashes for pico_ioc-2.0.0.tar.gz
Algorithm Hash digest
SHA256 ccfdcc0209f4a7405616176d6f2aa9b7f2097c088bdcd681f43c91fbd47dc4da
MD5 f3bc94768101b76213761a7efa42c2bc
BLAKE2b-256 ea55eec84f064763be83567801ed7855adbe77efeb9d993b3185fba0bff16dce

See more details on using hashes here.

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

Hashes for pico_ioc-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8f8adc91c7c0279045dc7790e61a2d18e3d342f81c9ef048df8a433ec6423e90
MD5 0053c49e5abe46479cb955b0d2ca427a
BLAKE2b-256 385618f984d75ef4ed759baa676e954ff4bdb91d3dd01ea51f1a866d1de54618

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page