Skip to content

API Reference

Complete API documentation for Pico-Boot.

Functions

init(*args, **kwargs) -> PicoContainer

Main entry point. Drop-in replacement for pico_ioc.init() with auto-discovery.

from pico_boot import init

container = init(
    modules=["myapp"],           # Required: modules to scan
    config=None,                 # Optional: ContextConfig
    profiles=(),                 # Optional: active profiles
    overrides={},                # Optional: component overrides for testing
    observers=[],                # Optional: ContainerObserver instances
    custom_scanners=[],          # Optional: additional CustomScanner instances
)

Parameters

Parameter Type Default Description
modules Iterable[str \| ModuleType] Required Modules to scan for components
config ContextConfig \| None None Configuration context (built via pico_ioc.configuration())
profiles tuple[str, ...] () Active profiles for conditional components
overrides dict[type, Any] {} Replace components (useful for testing)
observers list[ContainerObserver] [] Lifecycle observers
custom_scanners list[CustomScanner] [] Additional component scanners

Returns

PicoContainer - The initialized dependency injection container.

Behavior

  1. Normalizes and deduplicates provided modules
  2. Discovers plugins from pico_boot.modules entry points (if enabled)
  3. Merges user modules with discovered plugin modules
  4. Harvests PICO_SCANNERS from all loaded modules and merges with user-provided custom_scanners
  5. Delegates to pico_ioc.init() with enriched parameters

Example

from pico_boot import init

# Basic usage
container = init(modules=["myapp"])

# With profiles
container = init(modules=["myapp"], profiles=["production"])

# With test overrides
container = init(
    modules=["myapp"],
    overrides={DatabaseService: MockDatabase()}
)

# With custom configuration
from pico_ioc import configuration, EnvSource
container = init(
    modules=["myapp"],
    config=configuration(EnvSource(prefix="MYAPP_"))
)

Environment Variables

PICO_BOOT_AUTO_PLUGINS

Controls automatic plugin discovery.

Value Effect
true (default) Discover and load plugins
false, 0, no Disable plugin discovery
# Disable auto-discovery
export PICO_BOOT_AUTO_PLUGINS=false

Re-exported from pico-ioc

Pico-Boot re-exports these commonly used symbols for convenience:

Symbol Description
PicoContainer The container class
ContextConfig Configuration context type
ContainerObserver Observer protocol for lifecycle events

For all other symbols, import from pico_ioc:

from pico_ioc import component, provides, factory, configured
from pico_ioc import Qualifier, configure, cleanup
from pico_ioc import intercepted_by, MethodInterceptor, health
from pico_ioc import EventBus, Event, subscribe

Module Attributes

PICO_SCANNERS

A module-level list that plugins can define to provide custom scanners.

# my_plugin/__init__.py
from pico_ioc import CustomScanner

class MyScanner(CustomScanner):
    def scan(self, module):
        pass

PICO_SCANNERS = [MyScanner()]

Pico-Boot collects these from all loaded modules.


Entry Points

pico_boot.modules

The entry point group used for plugin discovery.

# pyproject.toml
[project.entry-points."pico_boot.modules"]
my_plugin = "my_plugin"

Format: <name> = "<module_path>"


Internal Functions

These are implementation details and may change:

Function Description
_to_module_list() Normalizes modules input to list
_import_module_like() Imports module from various input types
_normalize_modules() Deduplicates modules by name
_load_plugin_modules() Discovers entry point plugins
_harvest_scanners() Collects PICO_SCANNERS from modules

Logging

Pico-Boot uses the pico_boot logger:

import logging

# See plugin discovery
logging.getLogger("pico_boot").setLevel(logging.DEBUG)

# See all pico activity
logging.getLogger("pico_ioc").setLevel(logging.DEBUG)

Log messages:

Level Message
WARNING Plugin load failures

Type Hints

from typing import Any, Iterable, Union
from types import ModuleType

def init(
    modules: Union[Any, Iterable[Any]],
    config: ContextConfig | None = None,
    profiles: tuple[str, ...] = (),
    overrides: dict[type, Any] | None = None,
    observers: list[ContainerObserver] | None = None,
    custom_scanners: list[CustomScanner] | None = None,
) -> PicoContainer: ...

Version

Version is managed by setuptools-scm from git tags.

from pico_boot._version import __version__
print(__version__)

Auto-generated API

pico_boot

Pico-Boot: zero-configuration bootstrap layer for the Pico ecosystem.

Pico-Boot wraps pico_ioc.init() to add automatic plugin discovery via Python entry points and custom-scanner harvesting from PICO_SCANNERS module-level lists. It is a drop-in replacement for pico_ioc.init().

Typical usage::

from pico_boot import init

container = init(modules=["myapp"])  # scans recursively

Plugins that register the pico_boot.modules entry-point group are loaded automatically unless the PICO_BOOT_AUTO_PLUGINS environment variable is set to "false", "0", or "no".

init(*args, **kwargs)

Bootstrap a PicoContainer with automatic plugin discovery.

This is a drop-in replacement for pico_ioc.init(). It accepts exactly the same parameters and returns the same PicoContainer type. On top of what pico_ioc.init() does, pico_boot.init() performs three additional steps before delegating:

  1. Module normalisation -- the modules argument is coerced to a list, each element is imported if necessary, and duplicates are removed.
  2. Plugin auto-discovery -- unless disabled via the PICO_BOOT_AUTO_PLUGINS environment variable, all packages that register a pico_boot.modules entry point are imported and merged into the module list.
  3. Scanner harvesting -- every loaded module is inspected for a PICO_SCANNERS attribute. Any scanners found are appended to the custom_scanners parameter before the call to pico_ioc.init().

Parameters:

Name Type Description Default
*args Any

Positional arguments forwarded to pico_ioc.init(). Typically just modules.

()
**kwargs Any

Keyword arguments forwarded to pico_ioc.init(). Common keywords include:

  • modules (Union[Any, Iterable[Any]]) -- Modules to scan for components.
  • config (ContextConfig | None) -- Configuration context built via pico_ioc.configuration().
  • profiles (tuple[str, ...]) -- Active profiles for conditional component activation.
  • overrides (dict[type, Any] | None) -- Component overrides, useful for testing.
  • observers (list[ContainerObserver] | None) -- Lifecycle observers.
  • custom_scanners (list | None) -- Additional component scanners. Scanners harvested from PICO_SCANNERS are appended to this list.
{}

Returns:

Name Type Description
PicoContainer PicoContainer

The fully initialised dependency-injection

PicoContainer

container.

Raises:

Type Description
ImportError

If a user-specified module cannot be imported.

TypeError

If required arguments are missing.

Example

from pico_boot import init container = init(modules=["myapp"]) # scans recursively service = container.get(MyService)

Source code in src/pico_boot/__init__.py
def init(*args: Any, **kwargs: Any) -> PicoContainer:
    """Bootstrap a ``PicoContainer`` with automatic plugin discovery.

    This is a drop-in replacement for ``pico_ioc.init()``.  It
    accepts exactly the same parameters and returns the same
    ``PicoContainer`` type.  On top of what ``pico_ioc.init()``
    does, ``pico_boot.init()`` performs three additional steps
    before delegating:

    1. **Module normalisation** -- the *modules* argument is
       coerced to a list, each element is imported if necessary,
       and duplicates are removed.
    2. **Plugin auto-discovery** -- unless disabled via the
       ``PICO_BOOT_AUTO_PLUGINS`` environment variable, all
       packages that register a ``pico_boot.modules`` entry point
       are imported and merged into the module list.
    3. **Scanner harvesting** -- every loaded module is inspected
       for a ``PICO_SCANNERS`` attribute.  Any scanners found are
       appended to the *custom_scanners* parameter before the call
       to ``pico_ioc.init()``.

    Args:
        *args: Positional arguments forwarded to
            ``pico_ioc.init()``.  Typically just *modules*.
        **kwargs: Keyword arguments forwarded to
            ``pico_ioc.init()``.  Common keywords include:

            * **modules** (``Union[Any, Iterable[Any]]``) --
              Modules to scan for components.
            * **config** (``ContextConfig | None``) --
              Configuration context built via
              ``pico_ioc.configuration()``.
            * **profiles** (``tuple[str, ...]``) -- Active profiles
              for conditional component activation.
            * **overrides** (``dict[type, Any] | None``) --
              Component overrides, useful for testing.
            * **observers** (``list[ContainerObserver] | None``) --
              Lifecycle observers.
            * **custom_scanners** (``list | None``) -- Additional
              component scanners.  Scanners harvested from
              ``PICO_SCANNERS`` are appended to this list.

    Returns:
        PicoContainer: The fully initialised dependency-injection
        container.

    Raises:
        ImportError: If a user-specified module cannot be imported.
        TypeError: If required arguments are missing.

    Example:
        >>> from pico_boot import init
        >>> container = init(modules=["myapp"])  # scans recursively
        >>> service = container.get(MyService)
    """
    bound = _IOC_INIT_SIG.bind(*args, **kwargs)
    bound.apply_defaults()

    base_modules = _normalize_modules(_to_module_list(bound.arguments["modules"]))

    auto_flag = os.getenv("PICO_BOOT_AUTO_PLUGINS", "true").lower()
    auto_plugins = auto_flag not in ("0", "false", "no")

    if auto_plugins:
        plugin_modules = _load_plugin_modules()
        all_modules = _normalize_modules(list(base_modules) + plugin_modules)
    else:
        all_modules = base_modules

    bound.arguments["modules"] = all_modules

    harvested = _harvest_scanners(all_modules)
    if harvested:
        existing = bound.arguments.get("custom_scanners") or []
        bound.arguments["custom_scanners"] = list(existing) + harvested

    return _ioc_init(*bound.args, **bound.kwargs)