Skip to content

Dependency Annotations

Wireup uses type annotations to figure out which dependencies to inject. Most of the time, just the type is enough, but sometimes you need to add extra information using injection annotations.

When Do You Need Annotations?

Whether you need annotations depends on where you're injecting into Wireup services or into external targets.

Injecting into Wireup Services

For classes and functions marked with @service:

Dependency Type Annotations Needed? Required Information
Services No -
Interface with single implementation No -
Default implementation of interface No -
Interface with multiple implementations Yes Qualifier
Parameters Yes Parameter name
Parameter expressions Yes Expression template

Injecting into External Code

When you're injecting into code that Wireup doesn't manage (like framework route handlers), you always need annotations. Use either Annotated[T, Inject(...)] or the shorthand Injected[T].

Why external code needs annotations

Inside Wireup services, Wireup assumes full ownership of all dependencies, so basic Injected[T] annotations are redundant. For external targets, annotations explicitly tell Wireup to handle those parameters.

Usage Examples

Here's how to use annotations in Python 3.9+ (or Python 3.8+ with typing_extensions):

@wireup.inject_from_container(container)
def configure(
    # Inject a configuration parameter by name
    env: Annotated[str, Inject(param="app_env")],

    # Inject a computed value using parameter substitution
    log_path: Annotated[str, Inject(expr="${data_dir}/logs")],

    # Inject a service (explicit annotation required for external targets)
    service: Annotated[MyService, Inject()],

    # Alternative shorthand syntax for service injection
    service: Injected[MyService],

    # Inject a specific implementation when multiple exist
    db: Annotated[Database, Inject(qualifier="readonly")]
):
    ...