API Reference¶
Classes¶
TinyDiCtx¶
The dependency injection context.
@dataclass
class TinyDiCtx:
value_map: dict[str, Any]
fn_map: dict[Callable[..., Any], Callable[..., Any]]
validator: TypeValidator | None
depends_types: tuple[type, ...] = (Depends,)
| Attribute | Purpose |
|---|---|
value_map |
Values to inject by parameter name |
fn_map |
Function substitutions (for testing/mocking) |
validator |
Optional type validator (e.g., Pydantic) |
depends_types |
Tuple of Depends-like classes to recognize |
Methods¶
call_fn¶
Call a function with dependency injection. This is the main entry point.
# Basic call
result = await ctx.call_fn(my_function)
# With value injection
result = await ctx.call_fn(my_function, request_id=123)
# With dependency substitution (testing)
result = await ctx.call_fn(my_function, fn_map={real_db: mock_db})
# With validation
result = await ctx.call_fn(my_function, validator=my_validator)
# With multi-framework Depends recognition
from fastapi import Depends as FastApiDepends
result = await ctx.call_fn(my_function, depends_types=(Depends, FastApiDepends))
Parameters are merged with the context's existing maps. Cleanup of yield-based dependencies runs automatically.
with_maps¶
Create a new context with merged maps. The original context is not modified.
# Create a derived context
test_ctx = empty_di_ctx.with_maps(
fn_map={real_db: mock_db},
validator=pydantic_validator,
)
# Use the derived context
result = await test_ctx.call_fn(my_function, request_id=123)
# Create a context that recognizes multiple Depends classes
from fastapi import Depends as FastApiDepends
multi_ctx = empty_di_ctx.with_maps(
depends_types=(Depends, FastApiDepends)
)
Depends¶
Marks a parameter as a dependency.
@dataclass
class Depends:
dependency: Callable[..., Any] | None = None
use_cache: bool = True
scope: Any = None # FastAPI compatibility; ignored
| Attribute | Purpose | Default |
|---|---|---|
dependency |
The callable to invoke | None (infer from type) |
use_cache |
Cache result within a call_fn invocation |
True |
scope |
FastAPI compatibility (ignored by tiny-fastapi-di) | None |
Usage¶
# Explicit callable
def endpoint(db = Depends(get_db)): ...
# Infer from type
def endpoint(db: Annotated[Database, Depends()]): ...
# Disable cache
def endpoint(db = Depends(get_db, use_cache=False)): ...
TypeValidator (Protocol)¶
Protocol for custom validators.
@runtime_checkable
class TypeValidator(Protocol):
def validate(self, type_: type, value: Any) -> Any: ...
Implement this protocol to add custom validation or coercion. See Pydantic Integration for an example.
Instances¶
empty_di_ctx¶
Pre-configured empty context with no value_map, fn_map, or validator.
pydantic_di_ctx¶
Context with Pydantic validation (requires tiny-fastapi-di[pydantic]).
from tiny_fastapi_di.pydantic import pydantic_di_ctx
result = await pydantic_di_ctx.call_fn(my_function, user={"name": "Alice"})
Exceptions¶
All exceptions include actionable error messages.
RecursionError¶
Raised when circular dependencies are detected.
RecursionError: Circular dependency detected: get_user() is already being resolved.
Check the dependency chain for cycles.
TypeError¶
Raised when a dependency cannot be resolved.
TypeError: No value provided for required argument 'request_id'.
Provide via call_fn(**kwargs), Depends() default, or parameter default.
TypeError: Depends() for parameter 'db' has no callable.
Provide Depends(callable) or use Annotated[Type, Depends()] with a type annotation.
TypeError: Depends() requires a callable, got int: 42
FastAPI's Security Class¶
If you need FastAPI-compatible Security with OAuth2 scopes metadata, you can implement it yourself:
from dataclasses import dataclass, field
from tiny_fastapi_di import Depends
@dataclass
class Security(Depends):
"""Depends subclass with OAuth2 scopes metadata."""
scopes: list[str] = field(default_factory=list)
# Usage
async def endpoint(user = Security(get_user, scopes=["read", "write"])):
return user
The scopes field is metadata only. Enforcement is your responsibility, exactly as in FastAPI.