Skip to content

Integrations

We do not provide any fully supported 3rd party integrations as of this moment. However, di is designed to easily be integrated into existing frameworks.

Below are some samples that show how di might be used by web frameworks and other applications. These example are only for demonstration, and are missing critical features that would be required for a full fledged integration.

The integrations will be shown from a users perspective, but you can see the source code for the framework side in docs/src/.

Textual

Textual is a TUI (Text User Interface) framework for Python inspired by modern web development.

In this example, we add dependency injection functionality into Textual and use it to inject an HTTP client that pulls a markdown file from the web and displays it in the console. This example mirrors Textual's own simple.py example.

from dataclasses import dataclass

from httpx import AsyncClient
from rich.markdown import Markdown
from textual.widgets import Footer, Header, ScrollView  # type: ignore

from di import Depends
from docs.src.textual.src import App  # type: ignore


@dataclass
class Config:
    url: str = "https://raw.githubusercontent.com/willmcgugan/textual/main/examples/richreadme.md"


async def get_readme(
    config: Config, client: AsyncClient = Depends(scope="app", wire=False)
) -> Markdown:
    # URL could be loaded from config
    response = await client.get(config.url)
    response.raise_for_status()
    return Markdown(response.text, hyperlinks=True)


class GridTest(App):
    async def on_load(self) -> None:
        """Bind keys with the app loads (but before entering application mode)"""
        await self.bind("b", "view.toggle('sidebar')", "Toggle sidebar")
        await self.bind("q", "quit", "Quit")

    async def on_mount(self, readme: Markdown = Depends(get_readme)) -> None:
        """Create and dock the widgets."""

        # A scrollview to contain the markdown file
        body = ScrollView(gutter=1)

        # Header / footer / dock
        await self.view.dock(Header(), edge="top")
        await self.view.dock(Footer(), edge="bottom")

        # Dock the body in the remaining space
        await self.view.dock(body, edge="right")

        await self.call_later(body.update, readme)  # type: ignore


def main() -> None:
    GridTest.run(title="Grid Test", log="textual.log")  # type: ignore

Starlette

Starlette is a microframework with async support.

Adding dependency injection to Starlette is pretty straightforward. We just need to bind the incoming requests.

from dataclasses import dataclass

from starlette.requests import Request
from starlette.responses import Response
from starlette.testclient import TestClient

from di import Depends
from docs.src.starlette.src import App

app = App()


@dataclass
class Config:
    host: str = "localhost"  # could be loaded from env vars


@dataclass
class DBConnection:
    config: Config

    async def execute(self, stmt: str) -> None:
        print(f"Executing on {self.config.host}: {stmt}")


@app.get("/test")
async def route(request: Request, conn: DBConnection = Depends(scope="app")):
    await conn.execute((await request.body()).decode())
    return Response()


def main() -> None:
    with TestClient(app) as client:
        res = client.get("/test", data=b"SELECT 1")
        assert res.status_code == 200

A full implementation would also need ways to extract bodies, headers, etc. For an example of providing headers via dependency injection, see the Dependants docs.