Common dependency injection utilities for mountaineer & friends
Project description
mountaineer-di
Common dependency injection utilities for Mountaineer and related projects,
with pydantic as the only hard dependency.
This package provides a robust set of defining and injecting function dependencies.
It works on its own, and when FastAPI is
installed it can also interoperate with fastapi.Depends(...) and
request-bound parameters in the same dependency graph.
Installation
Install the package with uv:
uv add mountaineer-di
What It Does
mountaineer-di lets you declare dependencies on normal Python callables and
resolve them outside a framework request cycle.
It supports:
- Native
Depends(...)markers - Nested dependency graphs
- Seeded caller-provided kwargs
- Async and sync dependencies
- Generator and context-manager dependency lifecycles
- FastAPI request/query/path/header/cookie/body extraction when FastAPI is installed
- Runtime dependency overrides
- Callable-level dependency overrides via
@dependency_override(...)
Quick Start
Use Depends(...) to declare dependencies, then call the resolver and invoke
the target with the returned kwargs:
from typing import Annotated
from mountaineer_di import Depends, provide_dependencies
def get_prefix() -> str:
return "hello"
def get_message(prefix: str = Depends(get_prefix)) -> str:
return f"{prefix} world"
async def handler(message: Annotated[str, Depends(get_message)]) -> str:
return message
async with provide_dependencies(handler) as kwargs:
result = await handler(**kwargs)
print(result) # hello world
The resolver is an async context manager because generator dependencies and
returned context managers stay alive until the async with block exits.
Resolver Entry Points
There are two public ways to resolve a callable:
provide_dependencies(...)
This is the primary entry point:
async with provide_dependencies(
handler,
{"prefix": "hi"},
request=request,
path="/items/{item_id}",
dependency_overrides={original_dep: override_dep},
) as kwargs:
result = await handler(**kwargs)
Use it when you want the generic parameter names:
func: target callablekwargs: seeded values that should already exist in the dependency graphrequest: request-like object for request-aware resolutionpath: route template used to infer path parametersdependency_overrides: per-call override mapping
Native Usage
Seeded kwargs are available to nested dependencies before the handler runs:
from mountaineer_di import Depends, provide_dependencies
def get_message(prefix: str) -> str:
return f"{prefix} world"
async def handler(prefix: str, message: str = Depends(get_message)) -> str:
return message
async with provide_dependencies(handler, {"prefix": "seeded"}) as kwargs:
result = await handler(**kwargs)
print(result) # seeded world
Request-Bound Resolution
When FastAPI and Starlette are installed, the resolver can populate request parameters and FastAPI field markers:
from fastapi import Query, Request
from mountaineer_di import Depends, get_function_dependencies
def get_token(request: Request) -> str:
return request.headers["x-token"]
async def handler(
item_id: int,
q: str = Query(),
token: str = Depends(get_token),
) -> tuple[int, str, str]:
return (item_id, q, token)
async with get_function_dependencies(
callable=handler,
request=request,
url="/items/{item_id}",
) as kwargs:
result = await handler(**kwargs)
If the request contains GET /items/7?q=test, result becomes:
(7, "test", "<x-token header>")
FastAPI Interop
You can mix native and FastAPI dependency markers in the same graph:
from fastapi import Depends as FastAPIDepends, Request
from mountaineer_di import Depends, get_function_dependencies
def get_user_agent(request: Request) -> str | None:
return request.headers.get("user-agent")
def get_context(
user_agent: str | None = FastAPIDepends(get_user_agent),
) -> str:
return user_agent or "unknown"
async def task(context: str = Depends(get_context)) -> str:
return context
async with get_function_dependencies(
callable=task,
request=request,
) as kwargs:
result = await task(**kwargs)
Dependency Overrides
There are two ways to override dependencies.
Per-call overrides
Pass a dependency_overrides mapping when resolving a callable:
from mountaineer_di import Depends, provide_dependencies
def get_prefix() -> str:
return "original"
def get_message(prefix: str = Depends(get_prefix)) -> str:
return f"value:{prefix}"
def mocked_prefix() -> str:
return "mocked"
async with provide_dependencies(
get_message,
dependency_overrides={get_prefix: mocked_prefix},
) as kwargs:
result = get_message(**kwargs)
print(result) # value:mocked
Callable-level overrides
Use @dependency_override(...) when a specific callable should always resolve
with a local override. Stack multiple decorators to attach multiple local
overrides:
from mountaineer_di import Depends, dependency_override, provide_dependencies
def require_valid_user() -> str:
return "request-user"
def get_billing_user_from_request() -> str:
return "billing-user"
@dependency_override(require_valid_user, get_billing_user_from_request)
async def bill_for_metered_type(
user: str = Depends(require_valid_user),
) -> str:
return user
async with provide_dependencies(bill_for_metered_type) as kwargs:
result = await bill_for_metered_type(**kwargs)
print(result) # billing-user
Callable-level overrides are merged with per-call overrides. If the same dependency appears in both places, the explicit per-call override wins.
Development
Development commands are available through the repo Makefile, with lint,
ci-lint, lint-ruff, lint-ty, and test targets following the same
pattern as sibling Mountaineer repositories.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mountaineer_di-0.2.0.tar.gz.
File metadata
- Download URL: mountaineer_di-0.2.0.tar.gz
- Upload date:
- Size: 46.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43876e064f801f3f7cc67bfa5d05f48a4cd4955b06f198baf1c11e749d7a1d35
|
|
| MD5 |
0e379d6147244c1e3a26af929f1b414d
|
|
| BLAKE2b-256 |
8d69fd034bd3b7416f584b6fe569eb99c816d8c43e4590a84e8463c0a4848667
|
Provenance
The following attestation bundles were made for mountaineer_di-0.2.0.tar.gz:
Publisher:
ci.yml on piercefreeman/mountaineer-di
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mountaineer_di-0.2.0.tar.gz -
Subject digest:
43876e064f801f3f7cc67bfa5d05f48a4cd4955b06f198baf1c11e749d7a1d35 - Sigstore transparency entry: 1208046553
- Sigstore integration time:
-
Permalink:
piercefreeman/mountaineer-di@076a3063819b92b1b598e2cae18298f31329bfa6 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/piercefreeman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@076a3063819b92b1b598e2cae18298f31329bfa6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mountaineer_di-0.2.0-py3-none-any.whl.
File metadata
- Download URL: mountaineer_di-0.2.0-py3-none-any.whl
- Upload date:
- Size: 12.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
678a99a9b4fe4549d040e8ad5134a2e130d639fb16605713b1eb2c98c770146e
|
|
| MD5 |
1521491a060fbb8db60b8dcc23442ab8
|
|
| BLAKE2b-256 |
fcfb868118099c78960a986121b4a2eded12bbecd8983f4e97bdce7f6e496f74
|
Provenance
The following attestation bundles were made for mountaineer_di-0.2.0-py3-none-any.whl:
Publisher:
ci.yml on piercefreeman/mountaineer-di
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mountaineer_di-0.2.0-py3-none-any.whl -
Subject digest:
678a99a9b4fe4549d040e8ad5134a2e130d639fb16605713b1eb2c98c770146e - Sigstore transparency entry: 1208046598
- Sigstore integration time:
-
Permalink:
piercefreeman/mountaineer-di@076a3063819b92b1b598e2cae18298f31329bfa6 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/piercefreeman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@076a3063819b92b1b598e2cae18298f31329bfa6 -
Trigger Event:
push
-
Statement type: