# pico-ioc

Lightweight, async-native dependency injection container for Python 3.11+.

## Commands

```bash
pip install -e ".[test]"          # Install in dev mode
pytest tests/ -v                  # Run tests
pytest --cov=pico_ioc --cov-report=term-missing tests/  # Coverage
tox                               # Full matrix (3.11-3.14)
mkdocs serve -f mkdocs.yml        # Local docs
```

## Project Structure

```
src/pico_ioc/
  __init__.py          # Public API: decorators, init(), PicoContainer
  container.py         # PicoContainer - core DI container
  locator.py           # ComponentLocator - metadata registry
  scope.py             # ScopeManager, ScopedCaches, ContextVarScope
  resolver.py          # Dependency resolution & cycle detection
  scanner.py           # ComponentScanner - auto-discovery
  decorators.py        # @component, @factory, @provides, @configured, @intercepted_by
  constants.py         # PICO_META, PICO_INFRA, PICO_NAME, PICO_KEY
  interceptor.py       # MethodInterceptor, MethodCtx, proxy creation
  configuration.py     # DictSource, configuration(), @configured binding
  event_bus.py         # EventBus, @subscribe, publish/subscribe
  health.py            # HealthCheck protocol, HealthAggregator
  exceptions.py        # All custom exceptions
```

## Key Concepts

- **Decorators**: `@component`, `@factory`, `@provides`, `@configured`, `@intercepted_by`, `@subscribe`
- **Internal attributes**: `_pico_infra` (str: "component"/"configured"/"factory"/"provides"), `_pico_meta` (dict: scope, qualifier, etc.), `_pico_name`, `_pico_key`
- **Scopes**: `singleton`, `prototype`, `request`, `session`, `websocket`, `transaction` (built-in via ContextVarScope)
- **Resolution**: Constructor injection by type hint. Lists via qualifier. `container.get()` (sync) / `container.aget()` (async)
- **AOP**: `MethodInterceptor` protocol with `invoke(ctx, call_next)`. Applied via `@intercepted_by(InterceptorClass)`
- **Configuration**: `configuration(DictSource({...}))` creates config. `@configured(prefix="x", mapping="tree")` binds to dataclass
- **Entry point**: `init(modules=[...], config=cfg)` bootstraps container

## Code Style

- Python 3.11+ (type hints with `X | Y` union syntax)
- No docstrings on obvious methods
- Private attributes prefixed with `_`
- Internal pico attributes: `_pico_*` (never `__dunder__`)
- Async-first: prefer `async def` where IO is involved
- Exceptions in `exceptions.py`, not inline

## Testing

- pytest + pytest-asyncio (mode=strict)
- Tests mirror src structure: `test_container.py`, `test_scope.py`, etc.
- Use `DictSource` for test config, `sqlite+aiosqlite:///:memory:` for DB tests
- Target: >95% coverage

## Boundaries

- Do not modify `_version.py` (auto-generated by setuptools-scm)
- Do not add dependencies without discussion
- `pico-ioc` is the foundation - no circular deps with other pico-* packages
