# pico-ioc

> Lightweight, async-native dependency injection container for Python

Install: `pip install pico-ioc`. Import surface: `from pico_ioc import ...`.

## Usage

```python
import os
from dataclasses import dataclass
from pico_ioc import component, configured, configuration, init, EnvSource

# 1. Define configuration with @configured
@configured(prefix="APP_", mapping="auto")  # Auto-detects flat mapping
@dataclass
class Config:
    db_url: str = "sqlite:///demo.db"

# 2. Define components
@component
class Repo:
    def __init__(self, cfg: Config):  # Inject config
        self.cfg = cfg
    def fetch(self):
        return f"fetching from {self.cfg.db_url}"

@component
class Service:
    def __init__(self, repo: Repo):  # Inject Repo
        self.repo = repo
    def run(self):
        return self.repo.fetch()

# --- Example Setup ---
os.environ['APP_DB_URL'] = 'postgresql://user:pass@host/db'

# 3. Build configuration context
config_ctx = configuration(
    EnvSource(prefix="")  # Read APP_DB_URL from environment
)

# 4. Initialize container
container = init(modules=[__name__], config=config_ctx)  # Pass context via 'config'

# 5. Get and use the service
svc = container.get(Service)
print(svc.run())

# --- Cleanup ---
del os.environ['APP_DB_URL']
```

## Public API

- `class PicoError(Exception)` — Base exception for all pico-ioc errors.
- `class ProviderNotFoundError(PicoError)` — Raised when the container cannot find a provider for a requested key.
- `class ComponentCreationError(PicoError)` — Raised when a provider callable fails while creating a component.
- `class ScopeError(PicoError)` — Raised for scope-related errors (unknown scope, missing scope ID, reserved name).
- `class ConfigurationError(PicoError)` — Raised for configuration problems (missing keys, invalid sources, bad interpolation).
- `class SerializationError(PicoError)` — Raised when a proxy target cannot be serialized or deserialized.
- `class ValidationError(PicoError)` — Raised when startup validation detects wiring problems.
- `class InvalidBindingError(ValidationError)` — Raised when one or more dependency bindings are invalid.
- `class AsyncResolutionError(PicoError)` — Raised when ``get()`` encounters an awaitable result.
- `class EventBusClosedError(EventBusError)` — Raised when an operation is attempted on a closed EventBus.
- `component(cls=None, *, name: Any=None, qualifiers: Iterable[str]=(), scope: str='singleton', primary: bool=False, lazy: bool=False, conditional_profiles: Iterable[str]=(), conditional_require_env: Iterable[str]=(), conditional_predicate: Optional[Callable[[], bool]]=None, on_missing_selector: Optional[object]=None, on_missing_priority: int=0)` — Register a class as a container-managed component.
- `factory(cls=None, *, name: Any=None, qualifiers: Iterable[str]=(), scope: str='singleton', primary: bool=False, lazy: bool=False, conditional_profiles: Iterable[str]=(), conditional_require_env: Iterable[str]=(), conditional_predicate: Optional[Callable[[], bool]]=None, on_missing_selector: Optional[object]=None, on_missing_priority: int=0)` — Register a class as a factory that produces components via ``@provides`` methods.
- `provides(*dargs, **dkwargs)` — Mark a function or method as a component provider.
- `class Qualifier(str)` — A typed string used in ``Annotated`` hints for qualifier-based injection.
- `configure(fn)` — Mark a method as a post-construction lifecycle hook.
- `cleanup(fn)` — Mark a method as a shutdown lifecycle hook.
- `class ScopeProtocol` — Protocol for scope implementations.
- `class ContextVarScope(ScopeProtocol)` — Scope implementation backed by a :class:`contextvars.ContextVar`.
- `class ScopeManager` — Registry and coordinator for all scope implementations.
- `class ComponentLocator` — Read-only, queryable index of all registered component metadata.
- `class ScopedCaches` — Manages component instance storage across all scopes.
- `class ProviderMetadata` — Immutable descriptor for a registered provider.
- `class ComponentFactory` — Simple key-to-provider registry.
- `class DeferredProvider` — A provider whose execution is deferred until the container is ready.
- `Provider`
- `class MethodCtx` — Invocation context passed to interceptors.
- `class MethodInterceptor(Protocol)` — Protocol that interceptors must implement.
- `intercepted_by(*interceptor_classes: type['MethodInterceptor'])` — Decorator that attaches interceptors to a method.
- `class UnifiedComponentProxy(_ProxyProtocolMixin)` — Transparent proxy for lazy initialisation and AOP interception.
- `health(fn)` — Mark a method as a health-check endpoint.
- `class ContainerObserver(Protocol)` — Protocol for observing container resolution events.
- `class PicoContainer(_ResolutionMixin)` — The pico-ioc dependency injection container.
- `class EnvSource(ConfigSource)` — Configuration source backed by OS environment variables.
- `class FileSource(ConfigSource)` — Configuration source backed by a JSON file with flat key lookup.
- `class FlatDictSource(ConfigSource)` — Configuration source backed by an in-memory dictionary.
- `init(modules: Union[Any, Iterable[Any]], *, profiles: Tuple[str, ...]=(), allowed_profiles: Optional[Iterable[str]]=None, environ: Optional[Dict[str, str]]=None, overrides: Optional[Dict[KeyT, Any]]=None, logger: Optional[logging.Logger]=None, config: Optional[ContextConfig]=None, custom_scopes: Optional[Iterable[str]]=None, validate_only: bool=False, container_id: Optional[str]=None, observers: Optional[List[ContainerObserver]]=None, custom_scanners: Optional[List[CustomScanner]]=None)` — Bootstrap the pico-ioc container.
- `configured(target: Any='self', *, prefix: str='', mapping: str='auto', **kwargs)` — Bind a dataclass to configuration sources.
- `configuration(*sources: Any, overrides: Optional[Dict[str, Any]]=None)` — Build an immutable :class:`ContextConfig` from one or more sources.
- `class ContextConfig` — Immutable configuration object passed to :func:`init`.
- `class EventBus` — Asynchronous, typed, in-process event bus.
- `class ExecPolicy(Enum)` — Execution policy for event handlers.
- `class ErrorPolicy(Enum)` — Error handling policy for event handlers.
- `class Event` — Base class for application events.
- `class ConfigChanged(Event)` — Published by ``container.refresh_config()`` when tree sources changed.
- `subscribe(event_type: Type[Event], *, priority: int=0, policy: ExecPolicy=ExecPolicy.INLINE, once: bool=False)` — Decorator that marks a method for auto-subscription via ``AutoSubscriberMixin``.
- `class AutoSubscriberMixin` — Mixin that auto-subscribes ``@subscribe``-decorated methods to the EventBus.
- `class JsonTreeSource(TreeSource)` — Tree source that reads configuration from a JSON file.
- `class YamlTreeSource(TreeSource)` — Tree source that reads configuration from a YAML file.
- `class DictSource(TreeSource)` — Tree source backed by an in-memory dictionary.
- `expand_env(value: Any)` — Resolve ``${VAR}`` and ``${VAR:default}`` placeholders from the
- `class Discriminator` — Annotation marker for discriminated unions in tree configuration.
- `class Value` — Annotation marker that injects a literal value into a ``@configured`` field.
- `class DependencyRequest` — Describes a single dependency required by a constructor or method.
- `analyze_callable_dependencies(callable_obj: Callable[..., Any])` — Analyse a callable's signature and return its dependency requests.
- `class CustomScanner(Protocol)` — Protocol for extending component discovery.

## Docs

- docs/LEARN.md
- docs/adr/ (14 pages)
- docs/advanced-features/ (9 pages)
- docs/ai-ready.md
- docs/api-reference/ (11 pages)
- docs/architecture/ (7 pages)
- docs/cookbook/ (10 pages)
- docs/examples/ (2 pages)
- docs/faq.md
- docs/getting-started.md
- docs/how-to/ (3 pages)
- docs/migration.md
- docs/observability/ (5 pages)
- docs/skills.md
- docs/troubleshooting.md
- docs/user-guide/ (7 pages)
