Dependency injection helper for FastAPI
Project description
dipin
dipin provides a dependency injection container, that primarily aims to work well with FastAPI.
[!IMPORTANT]
dipin is in very early stages - the API will change, and functionality is surprisingly missing.
Motivation
FastAPI's dependency container works well, but can be messy to scale. Out of
the box, you can end up with many Annotated[X, Depends()]
. Some projects I've
worked on have a sprawling di.py, and references to Depends in non-FastAPI code.
dipin aims to:
- Remove the need to annotate types in non-FastAPI code with
Depends
, - Provide a simple API to access dependencies in route-handlers, e.g.:
from .di import DI @app.get("/") async def homepage(request: Request, user: DI[AuthenticatedUser]): ...
vsfrom .auth import get_authenticated_user @app.get("/") async def homepage(request: Request, user: Annotated[User, Depends(get_authenticated_user)]): ...
Architecture
TODO
Usage (alpha, buggy, will change)
# di.py
from dipin import Container
DI = Container()
# Register a singleton across all requests
from .settings import Settings # e.g. a pydantic-settings model
DI.register_instance(Settings(), Settings)
# Register a factory (lazy singleton), called once across all requests
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.ext.asyncio.engine import AsyncEngine
def create_engine(settings: DI[Settings]) -> AsyncEngine:
return create_async_engine(str(settings.database_dsn))
DI.register_factory(AsyncEngine, get_db_engine, create_once=True)
# Register a factory called per-request
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlalchemy.ext.asyncio import async_sessionmaker
async def create_session(engine: DI[AsyncEngine]) -> AsyncSession:
sessionmaker = async_sessionmaker(
bind=engine, class_=AsyncSession, expire_on_commit=False
)
async with sessionmaker() as session:
yield session
DI.register_factory(AsyncSession, create_session)
# app.py
from fastapi import FastAPI
from sqlmodel.ext.asyncio.session import AsyncSession
from .di import DI
app = FastAPI()
@app.get("/")
async def list_orders(session: DI[AsyncSession]):
qry = "SELECT * FROM orders"
res = await session.exec(qry)
orders = res.all()
return {"orders": orders}
Roadmap
- Autowiring
Currently, a factory function is needed for every non-instance dependency.
- Some dependencies can be auto-wired, e.g.:
class Foo: ... class Bar: def __init__(foo: Foo): ...
DI.register_factory(Foo) # Before def create_bar(foo: DI[Foo]): return Bar(foo) DI.register_factory(Bar, create_bar) # After DI.register_factory(Bar)
- It doesn't appear that the current method of creating
params.Depends
allows this (you get errors about the Foo dependency not present in the query-string). I think the approach is to generate these factory functions ourselves.
- Some dependencies can be auto-wired, e.g.:
- Support many types of dependencies
- Including named (but we won't be able to use
DI[...]
), and some of those described in python-dependency-injector. - Providing our own
fastapi.Depends
is likely needed.
- Including named (but we won't be able to use
- Supporting creation of dependencies with decorators
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
dipin-0.0.1.tar.gz
(6.3 kB
view hashes)
Built Distribution
dipin-0.0.1-py3-none-any.whl
(4.8 kB
view hashes)