Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ pip install that-depends
## Ecosystem

`that-depends` is part of the [`modern-python`](https://github.com/modern-python) family.
If you're starting a new project, consider [`modern-di`](https://github.com/modern-python/modern-di) —
If you're starting a new project, consider [`modern-di`](https://github.com/modern-python/modern-di),
the newer DI framework from the same author, with separate framework adapters:

- [`modern-di`](https://github.com/modern-python/modern-di) — core DI framework with scopes
- [`modern-di`](https://github.com/modern-python/modern-di): core DI framework with scopes
- [`modern-di-fastapi`](https://github.com/modern-python/modern-di-fastapi),
[`modern-di-litestar`](https://github.com/modern-python/modern-di-litestar),
[`modern-di-faststream`](https://github.com/modern-python/modern-di-faststream),
[`modern-di-typer`](https://github.com/modern-python/modern-di-typer),
[`modern-di-pytest`](https://github.com/modern-python/modern-di-pytest)

`that-depends` remains actively maintained — see the
`that-depends` remains actively maintained. See the
[migration guide](https://modern-di.modern-python.org/migration/from-that-depends/) if you
want to move existing projects across.

Expand All @@ -61,5 +61,4 @@ want to move existing projects across.

## Part of `modern-python`

Browse the full list of templates and libraries in
[`modern-python`](https://github.com/modern-python) — see the org profile for the categorized index.
The [`modern-python`](https://github.com/modern-python) org profile has the full categorized list of templates and libraries.
Binary file modified docs/assets/social-card.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions docs/ecosystem.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Ecosystem

`that-depends` is part of the [`modern-python`](https://github.com/modern-python) organization —
`that-depends` is part of the [`modern-python`](https://github.com/modern-python) organization,
a collection of open-source templates and libraries for production-ready Python applications.

## Newer DI framework: `modern-di`

If you're starting a new project, consider [`modern-di`](https://github.com/modern-python/modern-di) —
If you're starting a new project, consider [`modern-di`](https://github.com/modern-python/modern-di),
the newer DI framework from the same author. It ships as a small core plus a family of thin
framework adapters, in contrast to `that-depends`'s batteries-included approach.

Expand All @@ -28,13 +28,13 @@ walks through the API differences if you want to move an existing project across

End-to-end examples using `modern-di` for dependency injection:

- [`fastapi-sqlalchemy-template`](https://github.com/modern-python/fastapi-sqlalchemy-template) —
- [`fastapi-sqlalchemy-template`](https://github.com/modern-python/fastapi-sqlalchemy-template):
dockerized web application with DI on FastAPI, SQLAlchemy 2, PostgreSQL
- [`litestar-sqlalchemy-template`](https://github.com/modern-python/litestar-sqlalchemy-template) —
- [`litestar-sqlalchemy-template`](https://github.com/modern-python/litestar-sqlalchemy-template):
dockerized web application on LiteStar, SQLAlchemy 2, PostgreSQL

## Full project index

See the [`modern-python` organization profile](https://github.com/modern-python) for the
complete categorized list, including microservice utilities (`lite-bootstrap`, the
`faststream-*` family) and helper packages (`db-retry`, `eof-fixer`).
`faststream-*` family) and helper packages (`db-retry`, `eof-fixer`).
4 changes: 2 additions & 2 deletions docs/experimental/lazy.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lazy Provider
# Lazy provider

The `LazyProvider` enables you to reference other providers without explicitly
importing them into your module.
Expand All @@ -7,7 +7,7 @@ This can be helpful if you have a circular dependency between providers in
multiple containers.


## Creating a Lazy Provider
## Creating a lazy provider

=== "Single import string"
```python
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def create_async_resource():
logger.debug("Async resource destructed")
```

### Setup Dependency Injection Container with Providers
### Set up a dependency injection container with providers
```python
from that_depends import BaseContainer, providers

Expand Down
52 changes: 26 additions & 26 deletions docs/integrations/fastapi.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
# Usage with FastAPI

!!! info "See also"
[`modern-di-fastapi`](https://github.com/modern-python/modern-di-fastapi) — the equivalent
[`modern-di-fastapi`](https://github.com/modern-python/modern-di-fastapi) is the equivalent
FastAPI integration for [`modern-di`](https://github.com/modern-python/modern-di), the newer
sibling DI framework.

## Installation

To use **`that-depends`** with FastAPI, you need to install the package with the `fastapi` extra. You can do this using pip:
To use `that-depends` with FastAPI, you need to install the package with the `fastapi` extra. You can do this using pip:

```bash
pip install that-depends[fastapi]
```

---

## Creating a Container
## Creating a container

Suppose you have a simple container in a file called `mycontainer.py`:

Expand All @@ -38,9 +38,9 @@ Here, `MyContainer.current_time` is a provider that, when called, creates a new

---

## Using a custom Router class
## Using a custom router class

You can use the `create_fastapi_route_class()` method to create custom Route class for your application:
You can use the `create_fastapi_route_class()` method to create a custom route class for your application:

```python
from that_depends.integrations.fastapi import create_fastapi_route_class
Expand All @@ -58,7 +58,7 @@ router = APIRouter(route_class=my_route_class)

This will enable you to use dependency injection in your `FastAPI` endpoints:

> **Note**: If you don't want to use the custom router class, you can make use of `fastapi.Depends` instead.
> If you don't want to use the custom router class, you can use `fastapi.Depends` instead.


=== "Router class"
Expand Down Expand Up @@ -90,7 +90,7 @@ This will enable you to use dependency injection in your `FastAPI` endpoints:

### Managing container context

If you wish to initialize the container context you can simply pass arguments to `create_fastapi_route_class()`:
To initialize the container context, pass arguments to `create_fastapi_route_class()`:

```python
from that_depends import ContextScopes
Expand All @@ -103,16 +103,16 @@ and the global context will be set.



## Integrating with FastAPI Using DIContextMiddleware
## Integrating with FastAPI using DIContextMiddleware

The `DIContextMiddleware` can be used to manage context, but its features overlap with the [custom router class](#using-a-custom-router-class).
The main advantage of using middleware is that you can set it up for your entire `FastAPI` application.

> **Note:** If you want to use both the `DIContextMiddleware` and the custom router class, you should not pass any arguments to `create_fastapi_route_class()`.
> If you want to use both the `DIContextMiddleware` and the custom router class, do not pass any arguments to `create_fastapi_route_class()`.

### Setting Up the FastAPI App
### Setting up the FastAPI app

You can use **`that-depends`**’s `DIContextMiddleware` so that any request automatically initializes the context for your container(s). This approach is convenient if you want to:
You can use the `DIContextMiddleware` from `that-depends` so that any request automatically initializes the context for your container(s). This approach is convenient if you want to:

- Automatically initialize or tear down resources on each request.
- Provide a global or request-level context dictionary you can read from your container.
Expand Down Expand Up @@ -147,7 +147,7 @@ def get_time(
)
```

- **`DIContextMiddleware`** automatically sets a “global context” for every request.
- `DIContextMiddleware` automatically sets a global context for every request.
- The `Depends(MyContainer.current_time)` call is how you reference the container’s provider using the standard FastAPI injection system.

To run this app:
Expand All @@ -158,15 +158,15 @@ uvicorn main:app --reload

When you make a request to `/`, you will see the current time printed, and behind the scenes the that-depends container is in a context.

> **Note**: If your container uses advanced context-based resources (e.g. `ContextResource`), you may also set `default_scope` in your container, or configure an explicit scope. See the advanced section below.
> If your container uses context-based resources such as `ContextResource`, you can also set `default_scope` in your container or configure an explicit scope. See the advanced section below.

---



## Examples of different Providers in FastAPI
## Examples of different providers in FastAPI

### Singleton Providers
### Singleton providers

```python
# Suppose in mycontainer.py
Expand Down Expand Up @@ -195,7 +195,7 @@ def read_settings(settings: AppSettings = Depends(MyAdvancedContainer.settings))
return {"db_url": settings.database_url}
```

### Context Resources
### Context resources

For “request-scoped” resources (e.g. a DB connection per request), you can use `ContextResource` in your container. This typically works in conjunction with `DIContextMiddleware` or a manual `container_context(...)` call. For example:

Expand Down Expand Up @@ -247,7 +247,7 @@ async def read_db(

When writing unit tests, you can use `TestClient` from `starlette.testclient` or `pytest-asyncio` with standard FastAPI patterns. The `DIContextMiddleware` approach ensures resources are created and torn down automatically each request, so no special arrangement is necessary.

**Example**:
For example:

```python
import pytest
Expand All @@ -268,21 +268,21 @@ def test_read_db(client: TestClient):

---

## Common Patterns and Tips
## Common patterns and tips

1. **Global vs. Request Context**: Decide whether your container’s dependencies should be globally shared (e.g., singletons) or created anew per request (e.g., database or session).
2. **Combining with FastAPI’s `Depends`**: Generally, you can pass `Depends(MyContainer.some_provider)` to route handlers. Under the hood, that-depends will be invoked.
3. **Overriding**: You can override a provider in tests by calling `MyContainer.some_provider.override_sync(...)` or using the context manager `with MyContainer.some_provider.override_context_sync(...):`.
4. **Performance**: If you have expensive creation logic (like a DB engine that can be reused globally), prefer using a `Singleton` or `Object` provider. If you need ephemeral resources, use `ContextResource` with the `DIContextMiddleware`.
5. **Custom Context**: If you do not want to rely on the middleware, you can manually create a context in any async function by calling `async with container_context(MyContainer):`.
6. **Multiple Containers**: You can define multiple containers and connect them (e.g., `ContainerA.connect_containers(ContainerB)`), or add them all to the `DIContextMiddleware`. For advanced usage, see the that-depends documentation on “container connection.”
- Decide whether your container's dependencies should be globally shared (e.g., singletons) or created anew per request (e.g., database or session).
- You can generally pass `Depends(MyContainer.some_provider)` to route handlers, and `that-depends` resolves the provider under the hood.
- You can override a provider in tests by calling `MyContainer.some_provider.override_sync(...)` or using the context manager `with MyContainer.some_provider.override_context_sync(...):`.
- If you have expensive creation logic (like a DB engine that can be reused globally), prefer a `Singleton` or `Object` provider. If you need ephemeral resources, use `ContextResource` with the `DIContextMiddleware`.
- If you do not want to rely on the middleware, you can create a context manually in any async function by calling `async with container_context(MyContainer):`.
- You can define multiple containers and connect them (e.g., `ContainerA.connect_containers(ContainerB)`), or add them all to the `DIContextMiddleware`. See [Usage with multiple containers](../introduction/multiple-containers.md) for details.


### Accessing the FastAPI Request or Other Context Items
### Accessing the FastAPI request or other context items

Sometimes you want to pass the `fastapi.Request` (or other request-scoped data) into the container context so that providers can read it. You can do that either via the `DIContextMiddleware` (by customizing the `global_context` dynamically) or by writing your own dependency that calls `container_context()`.

**Example**: Writing a custom dependency that sets up the context with the current `Request`:
For example, a custom dependency that sets up the context with the current `Request`:

```python
# request_deps.py
Expand Down
10 changes: 5 additions & 5 deletions docs/integrations/faststream.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Usage with `FastStream`

!!! info "See also"
[`modern-di-faststream`](https://github.com/modern-python/modern-di-faststream) — the
[`modern-di-faststream`](https://github.com/modern-python/modern-di-faststream) is the
equivalent FastStream integration for [`modern-di`](https://github.com/modern-python/modern-di),
the newer sibling DI framework.

`that-depends` is out of the box compatible with `faststream.Depends()`:
`that-depends` works out of the box with `faststream.Depends()`:

```python hl_lines="14"
from typing import Annotated
Expand All @@ -30,10 +30,10 @@ async def process(
1. This would be the same as `Provide[Container.suffix_factory]`


## Context Middleware
## Context middleware

If you are using [ContextResource](../providers/context-resources.md) provider, you likely will want to
initialize a context before processing message with `faststream.`
If you are using [ContextResource](../providers/context-resources.md) provider, you will likely want to
initialize a context before processing messages with `faststream.`

`that-depends` provides integration for these use cases:

Expand Down
2 changes: 1 addition & 1 deletion docs/integrations/litestar.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Usage with `Litestar`

!!! info "See also"
[`modern-di-litestar`](https://github.com/modern-python/modern-di-litestar) — the equivalent
[`modern-di-litestar`](https://github.com/modern-python/modern-di-litestar) is the equivalent
Litestar integration for [`modern-di`](https://github.com/modern-python/modern-di), the newer
sibling DI framework.

Expand Down
16 changes: 7 additions & 9 deletions docs/introduction/generator-injection.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Injection into Generator Functions
# Injection into generator functions


`that-depends` supports dependency injections into generator functions. However, this comes
Expand Down Expand Up @@ -43,9 +43,9 @@ You can use the `@inject` decorator to inject dependencies into generator functi
yield value
```

## Supported Generators
## Supported generators

### Synchronous Generators
### Synchronous generators

`that-depends` supports injection into sync generator functions with the following signature:

Expand All @@ -60,7 +60,7 @@ This means that wrapping a sync generator with `@inject` will always preserve al
- It will raise `StopIteration` when the generator is exhausted or otherwise returns.


### Asynchronous Generators
### Asynchronous generators

`that-depends` supports injection into async generator functions with the following signature:

Expand All @@ -73,7 +73,7 @@ This means that wrapping an async generator with `@inject` will have the followi
- The generator will yield as expected
- The generator will **not** accept values via `asend()`

If you need to send values to an async generator, you can simply resolve dependencies in the generator body:
If you need to send values to an async generator, you can resolve dependencies in the generator body:

```python

Expand All @@ -95,7 +95,7 @@ as part of dependency injection into a generator.

This is the case for both async and sync injection.

**For example:**
For example:
```python
def sync_resource() -> typing.Iterator[float]:
yield random.random()
Expand Down Expand Up @@ -139,12 +139,10 @@ with container_context(scope=ContextScopes.REQUEST):
next(injected())
```

Since no context initialization was needed, the generator will work as expected.

1. Scope provided to `@inject` no longer matches scope of the `sync_provider`


### Container Context
### Container context

Similarly to above, the `@container_context` also does **not** support generators:

Expand Down
Loading
Loading