Skip to content

Function Injection

Note

When using the provided integrations, this is automatically handled for you. Only use this if you're injecting Wireup dependencies in a framework without an integration.

Instead of manually retrieving services via container.get or parameters via container.params, you can inject them directly into function parameters using the container as a decorator.

This transforms verbose manual dependency retrieval:

def client_function() -> None:
    random = container.get(RandomService)
    env_name = container.params.get("env")

    with container.enter_scope() as scoped_container:
        scoped_service = scoped_container.get(ScopedService)

Into clean, declarative dependency injection.

Using @wireup.inject_from_container

Use the wireup.inject_from_container decorator to automatically inject dependencies into function parameters. The container enters a scope before function execution, injects all dependencies, and exits the scope when the function returns.

Note

The decorator only injects parameters annotated with Injected[T] or Annotated[T, Inject()]. These annotations are equivalent—Injected[T] is simply an alias for convenience.

from wireup import Injected

@wireup.inject_from_container(container)
def client_function(
    service: Injected[RandomService], 
    scoped_service: Injected[ScopedService], 
    env_name: Annotated[str, Inject(param="env")]
) -> None: ...

Using an existing scoped container

If you have already created a scoped container elsewhere, provide a callable that returns it as the second argument. Wireup will use that container instead of creating a new scope:

scoped_container: ContextVar[ScopedSyncContainer] = ContextVar("scoped_container")

@wireup.inject_from_container(container, scoped_container.get)
def client_function(
    service: Injected[RandomService], 
    scoped_service: Injected[ScopedService], 
    env_name: Annotated[str, Inject(param="env")]
) -> None: ...

Creating a decorator alias

For cleaner code, you can alias the decorator:

scoped_container: ContextVar[ScopedSyncContainer] = ContextVar("scoped_container")
injected = wireup.inject_from_container(container, scoped_container.get)

@injected
def client_function(
    service: Injected[RandomService], 
    scoped_service: Injected[ScopedService], 
    env_name: Annotated[str, Inject(param="env")]
) -> None: ...

Additional notes

  • Works with both sync and async containers
  • For async def functions, use an async container created via wireup.create_async_container

API Reference

wireup.inject_from_container

Inject dependencies into the decorated function based on annotations.

Parameters:

Name Type Description Default
container SyncContainer | AsyncContainer

The main container instance created via wireup.create_sync_container or wireup.create_async_container.

required
scoped_container_supplier Callable[[], ScopedSyncContainer | ScopedAsyncContainer] | None

An optional callable that returns the current scoped container instance. If provided, it will be used to create scoped dependencies. If not provided, the container will automatically enter a scope. Provide a scoped_container_supplier if you need to manage the container's scope manually. For example, in web frameworks, you might enter the scope at the start of a request in middleware so that other middlewares can access the scoped container if needed.

None
middleware Callable[[ScopedSyncContainer | ScopedAsyncContainer, tuple[Any, ...], dict[str, Any]], AbstractContextManager[None]] | None

A context manager that wraps the execution of the target function.

None