Skip to content

Commit

Permalink
Add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ThirVondukr committed Feb 16, 2024
1 parent c0585a8 commit 16ad9fa
Show file tree
Hide file tree
Showing 24 changed files with 736 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ max_line_length = 79

[*.md]
trim_trailing_whitespace = false

[*.{yaml,yml}]
indent_size = 2
11 changes: 11 additions & 0 deletions aioinject/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,14 @@ async def __aexit__(

async def aclose(self) -> None:
await self.__aexit__(None, None, None)

def __enter__(self) -> Self:
return self

def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
self._singletons.__exit__(exc_type, exc_val, exc_tb)
20 changes: 20 additions & 0 deletions docs/code/contextmanagers/01_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import contextlib
from collections.abc import Iterator

import aioinject
from aioinject import Scoped


@contextlib.contextmanager
def dependency() -> Iterator[int]:
print("Startup")
yield 42
print("Shutdown")


container = aioinject.Container()
container.register(Scoped(dependency))

with container.sync_context() as ctx:
print(ctx.resolve(int)) # Startup, 42
# Shutdown
22 changes: 22 additions & 0 deletions docs/code/contextmanagers/02_container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import contextlib
from collections.abc import Iterator

import aioinject
from aioinject import Singleton


@contextlib.contextmanager
def dependency() -> Iterator[int]:
print("Startup")
yield 42
print("Shutdown")


container = aioinject.Container()
container.register(Singleton(dependency))

with container:
with container.sync_context() as ctx:
print(ctx.resolve(int)) # Startup, 42
print("Context is closed")
# Shutdown
26 changes: 26 additions & 0 deletions docs/code/contextmanagers/03_implicity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from types import TracebackType
from typing import Self

import aioinject
from aioinject import Scoped


class Class:
def __enter__(self) -> Self:
print("Startup")
return self

def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
print("Shutdown")


container = aioinject.Container()
container.register(Scoped(Class))

with container.sync_context() as ctx:
print(ctx.resolve(Class)) # <__main__.Class object at ...>
35 changes: 35 additions & 0 deletions docs/code/cookbook/pydantic-settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from collections.abc import Sequence

from pydantic import PostgresDsn
from pydantic_settings import BaseSettings, SettingsConfigDict

import aioinject


class AppSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="app_")

version: str
site_url: str


class DatabaseSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="database_")

url: PostgresDsn


_settings_classes: Sequence[type[BaseSettings]] = [
AppSettings,
DatabaseSettings,
]


def create_container() -> aioinject.Container:
container = aioinject.Container()

for settings_cls in _settings_classes:
# Type is inferred from the instance passed into "Object"
container.register(aioinject.Object(settings_cls()))

return container
34 changes: 34 additions & 0 deletions docs/code/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import aioinject


class Database:
def __init__(self) -> None:
self._storage = {1: "Username"}

def get(self, id: int) -> str | None:
return self._storage.get(id)


class UserService:
def __init__(
self,
database: Database, # <- Aioinject would try to inject `Database` here
) -> None:
self._database = database

def get(self, id: int) -> str:
user = self._database.get(id)
if user is None:
raise ValueError
return user


container = aioinject.Container()
container.register(aioinject.Singleton(Database))
container.register(aioinject.Singleton(UserService))

with container.sync_context() as ctx:
service = ctx.resolve(UserService)
user = service.get(1)
assert user == "Username"
print(user)
36 changes: 36 additions & 0 deletions docs/code/integrations/fastapi_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import contextlib
from collections.abc import AsyncIterator
from typing import Annotated

import uvicorn
from fastapi import FastAPI

import aioinject
from aioinject import Inject
from aioinject.ext.fastapi import AioInjectMiddleware, inject


container = aioinject.Container()
container.register(aioinject.Object(42))


@contextlib.asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
async with container:
yield


def create_app() -> FastAPI:
app = FastAPI(lifespan=lifespan)
app.add_middleware(AioInjectMiddleware, container=container)

@app.get("/")
@inject
async def root(number: Annotated[int, Inject]) -> int:
return number

return app


if __name__ == "__main__":
uvicorn.run("main:create_app", factory=True, reload=True)
31 changes: 31 additions & 0 deletions docs/code/integrations/litestar_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Annotated

import uvicorn
from litestar import Litestar, get

import aioinject
from aioinject import Inject
from aioinject.ext.litestar import AioInjectPlugin, inject


container = aioinject.Container()
container.register(aioinject.Object(42))


@get("/")
@inject
async def function_route(
number: Annotated[int, Inject],
) -> int:
return number


def create_app() -> Litestar:
return Litestar(
[function_route],
plugins=[AioInjectPlugin(container=container)],
)


if __name__ == "__main__":
uvicorn.run("main:create_app", factory=True, reload=True)
36 changes: 36 additions & 0 deletions docs/code/integrations/strawberry-graphql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import Annotated, Any

import strawberry
import uvicorn
from strawberry import Schema
from strawberry.asgi import GraphQL

import aioinject
from aioinject import Inject
from aioinject.ext.strawberry import AioInjectExtension, inject # (1)!


container = aioinject.Container()
container.register(aioinject.Object(42))


@strawberry.type
class Query:
@strawberry.field
@inject
async def number(self, number: Annotated[int, Inject]) -> int:
return number


def create_app() -> GraphQL[Any, Any]:
schema = Schema(
query=Query,
extensions=[
AioInjectExtension(container=container),
],
)
return GraphQL(schema=schema)


if __name__ == "__main__":
uvicorn.run("main:create_app", factory=True, reload=True)
10 changes: 10 additions & 0 deletions docs/code/providers/scoped.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import aioinject


container = aioinject.Container()
container.register(aioinject.Scoped(list))

with container.sync_context() as ctx:
object_1 = ctx.resolve(list)
object_2 = ctx.resolve(list)
assert object_1 is object_2
13 changes: 13 additions & 0 deletions docs/code/providers/singleton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import aioinject


container = aioinject.Container()
container.register(aioinject.Singleton(list))

with container.sync_context() as ctx:
object_1 = ctx.resolve(list)

with container.sync_context() as ctx:
object_2 = ctx.resolve(list)

assert object_1 is object_2
10 changes: 10 additions & 0 deletions docs/code/providers/transient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import aioinject


container = aioinject.Container()
container.register(aioinject.Transient(list))

with container.sync_context() as ctx:
object_1 = ctx.resolve(list)
object_2 = ctx.resolve(list)
assert object_1 is not object_2
41 changes: 41 additions & 0 deletions docs/context-managers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Dependencies can use
[`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager)
and [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager)
decorators to execute code during their init and shutdown.

## Implementation
Internally aioinject uses [contextlib.ExitStack](https://docs.python.org/3/library/contextlib.html#contextlib.ExitStack)
and [contextlib.AsyncExitStack](https://docs.python.org/3/library/contextlib.html#contextlib.AsyncExitStack)

## Providers
### Scoped and Transient providers
For `Scoped` and `Transient` providers dependencies will close when context is
closed:

```python
--8<-- "docs/code/contextmanagers/01_context.py"
```

### Singleton provider
In case of a `Singleton` they're closed when you close container itself:

```python
--8<-- "docs/code/contextmanagers/02_container.py"
```

## Using your own or 3rd party class as a context manager
Even if your class has `__enter__` and `__exit__` methods it won't implicitly be
used as a context manager:

```python
--8<-- "docs/code/contextmanagers/03_implicity.py"
```
Nothing is printed! You have to explicitly create a function and decorate it with
contextlib decorator:

```python
@contextlib.contextmanager
def create_class() -> Class:
with Class() as cls:
yield cls
```
7 changes: 7 additions & 0 deletions docs/cookbook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


## Pydantic Settings
Adding pydantic settings to a container:
```python
--8<-- "docs/code/cookbook/pydantic-settings.py"
```
14 changes: 14 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Aioinject is a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection)
and [service locator](https://en.wikipedia.org/wiki/Service_locator_pattern)
library, made to easily work with dependency injection using python type annotations.

## Installation
Install with pip or your favorite package manager:
```
pip install aioinject
```
## Example

```python
--8<-- "docs/code/example.py"
```
5 changes: 5 additions & 0 deletions docs/integrations/fastapi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
To integrate with FastAPI you need to add a `AioinjectMiddleware` and
optionally a lifespan if you use context manager dependencies:
```python hl_lines="24-25"
--8<-- "docs/code/integrations/fastapi_.py"
```
4 changes: 4 additions & 0 deletions docs/integrations/litestar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Litestar integration comes with a plugin, you just need to add it to litestar app:
```python hl_lines="26"
--8<-- "docs/code/integrations/litestar_.py"
```
8 changes: 8 additions & 0 deletions docs/integrations/strawberry-graphql.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Aioinject integrates with `strawberry-graphql` using a
[custom extension](https://strawberry.rocks/docs/guides/custom-extensions):

```python hl_lines="10 28"
--8<-- "docs/code/integrations/strawberry-graphql.py"
```

1. Note that `inject` is imported from `aioinject.ext.strawberry`
Loading

0 comments on commit 16ad9fa

Please sign in to comment.