Skip to content

Commit 43296b5

Browse files
committed
add docs initial structure
1 parent b6ab5c7 commit 43296b5

21 files changed

+1030
-0
lines changed

docs/css/code.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* Round the corners of code blocks */
2+
.md-typeset .highlight code {
3+
border-radius: 10px !important;
4+
}
5+
6+
@media (max-width: 600px) {
7+
.md-typeset code {
8+
border-radius: 4px;
9+
}
10+
}

docs/dev/contributing.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Contributing
2+
This is an opensource project, and we are opened to new contributors.
3+
4+
## Getting started
5+
1. Make sure that you have [uv](https://docs.astral.sh/uv/) and [just](https://just.systems/) installed.
6+
2. Clone project:
7+
```
8+
[email protected]:modern-python/modern-di.git
9+
cd modern-di
10+
```
11+
3. Install dependencies running `just install`
12+
13+
## Running linters
14+
`Ruff` and `mypy` are used for static analysis.
15+
16+
Run all checks by command `just lint`
17+
18+
## Running tests
19+
Run all tests by command `just test`

docs/dev/decisions.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Decisions
2+
1. Dependency resolving is async and sync:
3+
- if resolving requires event loop in sync mode `RuntimeError` is raised;
4+
- framework was developed mostly for usage with async python applications;
5+
- sync resolving is also possible, but it will fail in runtime in case of unresolved async dependencies;
6+
2. Resources and singletons are safe for concurrent resolving:
7+
- in async resolving `asyncio.Lock` is used;
8+
- in sync resolving `threading.Lock` is used;
9+
3. No global state -> all state lives in containers:
10+
- it's needed for scopes to work;
11+
4. Focus on maximum compatibility with mypy:
12+
- no need for `# type: ignore`
13+
- no need for `typing.cast`

docs/index.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Modern DI
2+
3+
Welcome to the `lite-bootstrap` documentation!
4+
5+
`lite-bootstrap` assists you in creating applications with all the necessary instruments already set up.
6+
7+
With `lite-bootstrap`, you receive an application with lightweight built-in support for:
8+
- `sentry`
9+
- `prometheus`
10+
- `opentelemetry`
11+
- `structlog`
12+
- `cors`
13+
- `swagger` - with additional offline version support
14+
- `health-checks`
15+
16+
Those instruments can be bootstrapped for:
17+
- `fastapi`,
18+
- `litestar`,
19+
- `faststream`,
20+
- services without these frameworks.
21+
22+
---

docs/integrations/fastapi.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Usage with `Fastapi`
2+
3+
*More advanced example of usage with FastAPI - [fastapi-sqlalchemy-template](https://github.com/modern-python/fastapi-sqlalchemy-template)*
4+
5+
## How to use
6+
7+
1. Install `modern-di-fastapi`:
8+
9+
=== "uv"
10+
11+
```bash
12+
uv add modern-di-fastapi
13+
```
14+
15+
=== "pip"
16+
17+
```bash
18+
pip install modern-di-fastapi
19+
```
20+
21+
=== "poetry"
22+
23+
```bash
24+
poetry add modern-di-fastapi
25+
```
26+
27+
2. Apply this code example to your application:
28+
```python
29+
import datetime
30+
import contextlib
31+
import typing
32+
33+
import fastapi
34+
import modern_di_fastapi
35+
from modern_di import Scope, providers
36+
37+
38+
app = fastapi.FastAPI()
39+
modern_di_fastapi.setup_di(app)
40+
41+
42+
async def create_async_resource() -> typing.AsyncIterator[datetime.datetime]:
43+
# async resource initiated
44+
try:
45+
yield datetime.datetime.now(tz=datetime.timezone.utc)
46+
finally:
47+
pass # async resource destructed
48+
49+
50+
async_resource = providers.Resource(Scope.APP, create_async_resource)
51+
52+
53+
@app.get("/")
54+
async def read_root(
55+
instance: typing.Annotated[
56+
datetime.datetime,
57+
modern_di_fastapi.FromDI(async_resource),
58+
],
59+
) -> datetime.datetime:
60+
return instance
61+
62+
```
63+
64+
## Websockets
65+
66+
Usually our application uses only two scopes: `APP` and `REQUEST`.
67+
68+
But when websockets are used, `SESSION` scope is used as well:
69+
- for the lifetime of websocket-connection we have `SESSION` scope
70+
- for each message we have `REQUEST` scope
71+
72+
`APP``SESSION``REQUEST`
73+
74+
`SESSION` scope is entered automatically.
75+
`REQUEST` scope must be entered manually:
76+
77+
```python
78+
import typing
79+
80+
import fastapi
81+
import modern_di
82+
import modern_di_fastapi
83+
84+
85+
app = fastapi.FastAPI()
86+
87+
88+
@app.websocket("/ws")
89+
async def websocket_endpoint(
90+
websocket: fastapi.WebSocket,
91+
session_container: typing.Annotated[modern_di.Container, fastapi.Depends(modern_di_fastapi.build_di_container)],
92+
) -> None:
93+
with session_container.build_child_container() as request_container:
94+
# REQUEST scope is entered here
95+
pass
96+
97+
await websocket.accept()
98+
await websocket.send_text("test")
99+
await websocket.close()
100+
```

docs/integrations/faststream.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Usage with `FastStream`
2+
3+
## How to use
4+
5+
1. Install `modern-di-faststream`:
6+
7+
=== "uv"
8+
9+
```bash
10+
uv add modern-di-faststream
11+
```
12+
13+
=== "pip"
14+
15+
```bash
16+
pip install modern-di-faststream
17+
```
18+
19+
=== "poetry"
20+
21+
```bash
22+
poetry add modern-di-faststream
23+
```
24+
25+
2. Apply this code example to your application:
26+
27+
```python
28+
import datetime
29+
import typing
30+
31+
import faststream
32+
from faststream.nats import NatsBroker
33+
import modern_di_faststream
34+
from modern_di import Scope, providers
35+
36+
37+
broker = NatsBroker()
38+
app = faststream.FastStream(broker=broker)
39+
modern_di_faststream.setup_di(app)
40+
41+
42+
async def create_async_resource() -> typing.AsyncIterator[datetime.datetime]:
43+
# async resource initiated
44+
try:
45+
yield datetime.datetime.now(tz=datetime.timezone.utc)
46+
finally:
47+
pass # async resource destructed
48+
49+
50+
async_resource = providers.Resource(Scope.APP, create_async_resource)
51+
52+
53+
@broker.subscriber("in")
54+
async def read_root(
55+
instance: typing.Annotated[
56+
datetime.datetime,
57+
modern_di_faststream.FromDI(async_resource),
58+
],
59+
) -> datetime.datetime:
60+
return instance
61+
62+
```

docs/integrations/litestar.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Usage with `Litestar`
2+
3+
*More advanced example of usage with LiteStar - [litestar-sqlalchemy-template](https://github.com/modern-python/litestar-sqlalchemy-template)*
4+
5+
## How to use
6+
7+
1. Install `modern-di-litestar`:
8+
9+
=== "uv"
10+
11+
```bash
12+
uv add modern-di-litestar
13+
```
14+
15+
=== "pip"
16+
17+
```bash
18+
pip install modern-di-litestar
19+
```
20+
21+
=== "poetry"
22+
23+
```bash
24+
poetry add modern-di-litestar
25+
```
26+
27+
2. Apply this code example to your application:
28+
```python
29+
import datetime
30+
import typing
31+
32+
from litestar import Litestar, get
33+
import modern_di_litestar
34+
from modern_di import Scope, providers
35+
36+
37+
async def create_async_resource() -> typing.AsyncIterator[datetime.datetime]:
38+
# async resource initiated
39+
try:
40+
yield datetime.datetime.now(tz=datetime.timezone.utc)
41+
finally:
42+
pass # async resource destructed
43+
44+
45+
async_resource = providers.Resource(Scope.APP, create_async_resource)
46+
47+
48+
@get("/", dependencies={"injected": modern_di_litestar.FromDI(async_resource)})
49+
async def index(injected: datetime.datetime) -> str:
50+
return injected.isoformat()
51+
52+
53+
app = Litestar(
54+
route_handlers=[index],
55+
plugins=[modern_di_litestar.ModernDIPlugin()],
56+
)
57+
```
58+
59+
## Websockets
60+
61+
Usually our application uses only two scopes: `APP` and `REQUEST`.
62+
63+
But when websockets are used, `SESSION` scope is used as well:
64+
- for the lifetime of websocket-connection we have `SESSION` scope
65+
- for each message we have `REQUEST` scope
66+
67+
`APP``SESSION``REQUEST`
68+
69+
`SESSION` scope is entered automatically.
70+
`REQUEST` scope must be entered manually:
71+
72+
```python
73+
import litestar
74+
import modern_di
75+
import modern_di_litestar
76+
77+
78+
app = litestar.Litestar(plugins=[modern_di_litestar.ModernDIPlugin()])
79+
80+
81+
@litestar.websocket_listener("/ws")
82+
async def websocket_handler(data: str, di_container: modern_di.Container) -> None:
83+
with di_container.build_child_container() as request_container:
84+
# REQUEST scope is entered here
85+
pass
86+
87+
app.register(websocket_handler)
88+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Application settings
2+
For example, you have application settings in `pydantic_settings`
3+
```python
4+
import pydantic_settings
5+
6+
7+
class Settings(pydantic_settings.BaseSettings):
8+
service_name: str = "FastAPI template"
9+
debug: bool = False
10+
...
11+
```
12+
13+
You can register settings as `Singleton` in DI container
14+
15+
```python
16+
from modern_di import BaseGraph, Scope, providers
17+
18+
19+
class DIContainer(BaseGraph):
20+
settings = providers.Singleton(Scope.APP, Settings)
21+
some_factory = providers.Factory(Scope.APP, SomeFactory, service_name=settings.cast.service_name)
22+
```
23+
24+
And when `some_factory` is resolved it will receive `service_name` attribute from `Settings`

docs/introduction/context-data.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Context data
2+
3+
Often, scopes are connected with external events: HTTP-requests, message from queue, callbacks from framework.
4+
5+
These events can be represented by some objects which can be used for dependencies creation.
6+
7+
There are several providers with access to such context data. You can read more [here](../../providers/context-providers):

0 commit comments

Comments
 (0)