From 16ad9fa806ff26eed2e6a05dc2e9245523142230 Mon Sep 17 00:00:00 2001 From: Doctor Date: Fri, 16 Feb 2024 15:11:49 +0300 Subject: [PATCH] Add docs --- .editorconfig | 3 + aioinject/containers.py | 11 + docs/code/contextmanagers/01_context.py | 20 ++ docs/code/contextmanagers/02_container.py | 22 ++ docs/code/contextmanagers/03_implicity.py | 26 ++ docs/code/cookbook/pydantic-settings.py | 35 +++ docs/code/example.py | 34 +++ docs/code/integrations/fastapi_.py | 36 +++ docs/code/integrations/litestar_.py | 31 ++ docs/code/integrations/strawberry-graphql.py | 36 +++ docs/code/providers/scoped.py | 10 + docs/code/providers/singleton.py | 13 + docs/code/providers/transient.py | 10 + docs/context-managers.md | 41 +++ docs/cookbook.md | 7 + docs/index.md | 14 + docs/integrations/fastapi.md | 5 + docs/integrations/litestar.md | 4 + docs/integrations/strawberry-graphql.md | 8 + docs/providers.md | 33 +++ examples/fastapi.md | 5 +- mkdocs.yml | 43 +++ pdm.lock | 289 ++++++++++++++++++- pyproject.toml | 13 +- 24 files changed, 736 insertions(+), 13 deletions(-) create mode 100644 docs/code/contextmanagers/01_context.py create mode 100644 docs/code/contextmanagers/02_container.py create mode 100644 docs/code/contextmanagers/03_implicity.py create mode 100644 docs/code/cookbook/pydantic-settings.py create mode 100644 docs/code/example.py create mode 100644 docs/code/integrations/fastapi_.py create mode 100644 docs/code/integrations/litestar_.py create mode 100644 docs/code/integrations/strawberry-graphql.py create mode 100644 docs/code/providers/scoped.py create mode 100644 docs/code/providers/singleton.py create mode 100644 docs/code/providers/transient.py create mode 100644 docs/context-managers.md create mode 100644 docs/cookbook.md create mode 100644 docs/index.md create mode 100644 docs/integrations/fastapi.md create mode 100644 docs/integrations/litestar.md create mode 100644 docs/integrations/strawberry-graphql.md create mode 100644 docs/providers.md create mode 100644 mkdocs.yml diff --git a/.editorconfig b/.editorconfig index 7a6777f..045db2c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,6 @@ max_line_length = 79 [*.md] trim_trailing_whitespace = false + +[*.{yaml,yml}] +indent_size = 2 diff --git a/aioinject/containers.py b/aioinject/containers.py index 83fca30..133c881 100644 --- a/aioinject/containers.py +++ b/aioinject/containers.py @@ -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) diff --git a/docs/code/contextmanagers/01_context.py b/docs/code/contextmanagers/01_context.py new file mode 100644 index 0000000..c2d8393 --- /dev/null +++ b/docs/code/contextmanagers/01_context.py @@ -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 diff --git a/docs/code/contextmanagers/02_container.py b/docs/code/contextmanagers/02_container.py new file mode 100644 index 0000000..5170d55 --- /dev/null +++ b/docs/code/contextmanagers/02_container.py @@ -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 diff --git a/docs/code/contextmanagers/03_implicity.py b/docs/code/contextmanagers/03_implicity.py new file mode 100644 index 0000000..e957c23 --- /dev/null +++ b/docs/code/contextmanagers/03_implicity.py @@ -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 ...> diff --git a/docs/code/cookbook/pydantic-settings.py b/docs/code/cookbook/pydantic-settings.py new file mode 100644 index 0000000..778b193 --- /dev/null +++ b/docs/code/cookbook/pydantic-settings.py @@ -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 diff --git a/docs/code/example.py b/docs/code/example.py new file mode 100644 index 0000000..ca56564 --- /dev/null +++ b/docs/code/example.py @@ -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) diff --git a/docs/code/integrations/fastapi_.py b/docs/code/integrations/fastapi_.py new file mode 100644 index 0000000..fc8d67c --- /dev/null +++ b/docs/code/integrations/fastapi_.py @@ -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) diff --git a/docs/code/integrations/litestar_.py b/docs/code/integrations/litestar_.py new file mode 100644 index 0000000..f63b92a --- /dev/null +++ b/docs/code/integrations/litestar_.py @@ -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) diff --git a/docs/code/integrations/strawberry-graphql.py b/docs/code/integrations/strawberry-graphql.py new file mode 100644 index 0000000..53fc3e8 --- /dev/null +++ b/docs/code/integrations/strawberry-graphql.py @@ -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) diff --git a/docs/code/providers/scoped.py b/docs/code/providers/scoped.py new file mode 100644 index 0000000..da59b80 --- /dev/null +++ b/docs/code/providers/scoped.py @@ -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 diff --git a/docs/code/providers/singleton.py b/docs/code/providers/singleton.py new file mode 100644 index 0000000..f19c3df --- /dev/null +++ b/docs/code/providers/singleton.py @@ -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 diff --git a/docs/code/providers/transient.py b/docs/code/providers/transient.py new file mode 100644 index 0000000..5dfc30e --- /dev/null +++ b/docs/code/providers/transient.py @@ -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 diff --git a/docs/context-managers.md b/docs/context-managers.md new file mode 100644 index 0000000..38f4140 --- /dev/null +++ b/docs/context-managers.md @@ -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 +``` diff --git a/docs/cookbook.md b/docs/cookbook.md new file mode 100644 index 0000000..581adb6 --- /dev/null +++ b/docs/cookbook.md @@ -0,0 +1,7 @@ + + +## Pydantic Settings +Adding pydantic settings to a container: +```python +--8<-- "docs/code/cookbook/pydantic-settings.py" +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..cf95124 --- /dev/null +++ b/docs/index.md @@ -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" +``` diff --git a/docs/integrations/fastapi.md b/docs/integrations/fastapi.md new file mode 100644 index 0000000..acd4207 --- /dev/null +++ b/docs/integrations/fastapi.md @@ -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" +``` diff --git a/docs/integrations/litestar.md b/docs/integrations/litestar.md new file mode 100644 index 0000000..b8201c8 --- /dev/null +++ b/docs/integrations/litestar.md @@ -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" +``` diff --git a/docs/integrations/strawberry-graphql.md b/docs/integrations/strawberry-graphql.md new file mode 100644 index 0000000..11646fa --- /dev/null +++ b/docs/integrations/strawberry-graphql.md @@ -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` diff --git a/docs/providers.md b/docs/providers.md new file mode 100644 index 0000000..6bd641c --- /dev/null +++ b/docs/providers.md @@ -0,0 +1,33 @@ +Aioinject implements multiple different providers, +they work similarly to [service lifetimes](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#service-lifetimes) +in other libraries, such as DI in .NET Core + +### Scoped + +Objects provided with `Scoped` provider would be cached within context. +```python +--8<-- "docs/code/providers/scoped.py" +``` + +### Transient + +`Transient` provider would provide different instances even if used within same context +```python +--8<-- "docs/code/providers/transient.py" +``` + +### Singleton + +`Singleton` works as you expect - there would be only one instance of a singleton +object, bound to a specific container +```python +--8<-- "docs/code/providers/singleton.py" +``` + +### Object + +`Object` provider just returns an object provided to it: +```python +aioinject.Object(42) +``` +would always return 42 diff --git a/examples/fastapi.md b/examples/fastapi.md index 97d4c58..2e0eaa9 100644 --- a/examples/fastapi.md +++ b/examples/fastapi.md @@ -16,8 +16,8 @@ container.register(aioinject.Object(42)) @contextlib.asynccontextmanager async def lifespan(_: FastAPI) -> AsyncIterator[None]: - async with contextlib.aclosing(container): - yield + async with container: + pass def create_app() -> FastAPI: @@ -35,5 +35,4 @@ def create_app() -> FastAPI: if __name__ == "__main__": uvicorn.run("main:create_app", factory=True, reload=True) - ``` diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..a936ad3 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,43 @@ +site_name: Aioinject +site_description: Async-first python dependency injection library +repo_url: https://github.com/ThirVondukr/aioinject + +nav: + - About: index.md + - Providers: providers.md + - Context manager dependencies: context-managers.md + - Integrations: + - FastAPI: integrations/fastapi.md + - Litestar: integrations/litestar.md + - Strawberry GraphQL: integrations/strawberry-graphql.md + - Cook Book: cookbook.md + + +theme: + name: material + features: + - content.code.annotate + - content.code.copy + - content.code.select + + palette: + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: pink + toggle: + icon: material/brightness-4 + + - media: "(prefers-color-scheme: light)" + scheme: default + primary: pink + toggle: + icon: material/brightness-7 + +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences diff --git a/pdm.lock b/pdm.lock index b46a54d..85b92e3 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,10 +2,10 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "dev"] +groups = ["default", "dev", "docs"] strategy = ["cross_platform"] lock_version = "4.4.1" -content_hash = "sha256:4b3e7a7608869d2aa7fc37966186710b1336efb131d1cbe7493cb8111d2e2a88" +content_hash = "sha256:b4eb04abf3b245850eeed52927a0a299ccbf0aa8cd9cda8fd084ac3c976552d1" [[package]] name = "annotated-types" @@ -66,6 +66,16 @@ files = [ {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] +[[package]] +name = "babel" +version = "2.14.0" +requires_python = ">=3.7" +summary = "Internationalization utilities" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + [[package]] name = "black" version = "23.12.1" @@ -372,17 +382,29 @@ files = [ [[package]] name = "fastapi" -version = "0.108.0" +version = "0.109.2" requires_python = ">=3.8" summary = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" dependencies = [ "pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4", - "starlette<0.33.0,>=0.29.0", + "starlette<0.37.0,>=0.36.3", "typing-extensions>=4.8.0", ] files = [ - {file = "fastapi-0.108.0-py3-none-any.whl", hash = "sha256:8c7bc6d315da963ee4cdb605557827071a9a7f95aeb8fcdd3bde48cdc8764dd7"}, - {file = "fastapi-0.108.0.tar.gz", hash = "sha256:5056e504ac6395bf68493d71fcfc5352fdbd5fda6f88c21f6420d80d81163296"}, + {file = "fastapi-0.109.2-py3-none-any.whl", hash = "sha256:2c9bab24667293b501cad8dd388c05240c850b58ec5876ee3283c47d6e1e3a4d"}, + {file = "fastapi-0.109.2.tar.gz", hash = "sha256:f3817eac96fe4f65a2ebb4baa000f394e55f5fccdaf7f75250804bc58f354f73"}, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +summary = "Copy your docs directly to the gh-pages branch." +dependencies = [ + "python-dateutil>=2.8.1", +] +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] [[package]] @@ -504,6 +526,16 @@ files = [ {file = "litestar-2.5.0.tar.gz", hash = "sha256:b88780d538a387dab4e02a94de2089822d829dba6a0666abc37593b07d479e00"}, ] +[[package]] +name = "markdown" +version = "3.5.2" +requires_python = ">=3.8" +summary = "Python implementation of John Gruber's Markdown." +files = [ + {file = "Markdown-3.5.2-py3-none-any.whl", hash = "sha256:d43323865d89fc0cb9b20c75fc8ad313af307cc087e84b657d9eec768eddeadd"}, + {file = "Markdown-3.5.2.tar.gz", hash = "sha256:e1ac7b3dc550ee80e602e71c1d168002f062e49f1b11e26a36264dafd4df2ef8"}, +] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -566,6 +598,74 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "mergedeep" +version = "1.3.4" +requires_python = ">=3.6" +summary = "A deep merge function for 🐍." +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.5.3" +requires_python = ">=3.7" +summary = "Project documentation with Markdown." +dependencies = [ + "click>=7.0", + "colorama>=0.4; platform_system == \"Windows\"", + "ghp-import>=1.0", + "jinja2>=2.11.1", + "markdown>=3.2.1", + "markupsafe>=2.0.1", + "mergedeep>=1.3.4", + "packaging>=20.5", + "pathspec>=0.11.1", + "platformdirs>=2.2.0", + "pyyaml-env-tag>=0.1", + "pyyaml>=5.1", + "watchdog>=2.0", +] +files = [ + {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"}, + {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"}, +] + +[[package]] +name = "mkdocs-material" +version = "9.5.9" +requires_python = ">=3.8" +summary = "Documentation that simply works" +dependencies = [ + "babel~=2.10", + "colorama~=0.4", + "jinja2~=3.0", + "markdown~=3.2", + "mkdocs-material-extensions~=1.3", + "mkdocs~=1.5.3", + "paginate~=0.5", + "pygments~=2.16", + "pymdown-extensions~=10.2", + "regex>=2022.4", + "requests~=2.26", +] +files = [ + {file = "mkdocs_material-9.5.9-py3-none-any.whl", hash = "sha256:a5d62b73b3b74349e45472bfadc129c871dd2d4add68d84819580597b2f50d5d"}, + {file = "mkdocs_material-9.5.9.tar.gz", hash = "sha256:635df543c01c25c412d6c22991872267723737d5a2f062490f33b2da1c013c6d"}, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown and MkDocs Material." +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + [[package]] name = "msgspec" version = "0.18.5" @@ -698,6 +798,14 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] +[[package]] +name = "paginate" +version = "0.5.6" +summary = "Divides large result sets into pages for easier browsing" +files = [ + {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -860,6 +968,20 @@ files = [ {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, ] +[[package]] +name = "pydantic-settings" +version = "2.1.0" +requires_python = ">=3.8" +summary = "Settings management using Pydantic" +dependencies = [ + "pydantic>=2.3.0", + "python-dotenv>=0.21.0", +] +files = [ + {file = "pydantic_settings-2.1.0-py3-none-any.whl", hash = "sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"}, + {file = "pydantic_settings-2.1.0.tar.gz", hash = "sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c"}, +] + [[package]] name = "pygments" version = "2.17.2" @@ -870,6 +992,20 @@ files = [ {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] +[[package]] +name = "pymdown-extensions" +version = "10.7" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown." +dependencies = [ + "markdown>=3.5", + "pyyaml", +] +files = [ + {file = "pymdown_extensions-10.7-py3-none-any.whl", hash = "sha256:6ca215bc57bc12bf32b414887a68b810637d039124ed9b2e5bd3325cbb2c050c"}, + {file = "pymdown_extensions-10.7.tar.gz", hash = "sha256:c0d64d5cf62566f59e6b2b690a4095c931107c250a8c8e1351c1de5f6b036deb"}, +] + [[package]] name = "pytest" version = "7.4.4" @@ -901,6 +1037,16 @@ files = [ {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] +[[package]] +name = "python-dotenv" +version = "1.0.1" +requires_python = ">=3.8" +summary = "Read key-value pairs from a .env file and set them as environment variables" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + [[package]] name = "pyyaml" version = "6.0.1" @@ -932,6 +1078,19 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +requires_python = ">=3.6" +summary = "A custom YAML tag for referencing environment variables in YAML files. " +dependencies = [ + "pyyaml", +] +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + [[package]] name = "questionary" version = "2.0.1" @@ -945,6 +1104,77 @@ files = [ {file = "questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b"}, ] +[[package]] +name = "regex" +version = "2023.12.25" +requires_python = ">=3.7" +summary = "Alternative regular expression module, to replace re." +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +requires_python = ">=3.7" +summary = "Python HTTP for Humans." +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + [[package]] name = "rich" version = "13.7.0" @@ -1030,15 +1260,15 @@ files = [ [[package]] name = "starlette" -version = "0.32.0.post1" +version = "0.36.3" requires_python = ">=3.8" summary = "The little ASGI library that shines." dependencies = [ "anyio<5,>=3.4.0", ] files = [ - {file = "starlette-0.32.0.post1-py3-none-any.whl", hash = "sha256:cd0cb10ddb49313f609cedfac62c8c12e56c7314b66d89bb077ba228bada1b09"}, - {file = "starlette-0.32.0.post1.tar.gz", hash = "sha256:e54e2b7e2fb06dff9eac40133583f10dfa05913f5a85bf26f427c7a40a9a3d02"}, + {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"}, + {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, ] [[package]] @@ -1115,6 +1345,16 @@ files = [ {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] +[[package]] +name = "urllib3" +version = "2.2.0" +requires_python = ">=3.8" +summary = "HTTP library with thread-safe connection pooling, file post, and more." +files = [ + {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, + {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, +] + [[package]] name = "uvicorn" version = "0.25.0" @@ -1130,6 +1370,37 @@ files = [ {file = "uvicorn-0.25.0.tar.gz", hash = "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2"}, ] +[[package]] +name = "watchdog" +version = "4.0.0" +requires_python = ">=3.8" +summary = "Filesystem events monitoring" +files = [ + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, + {file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"}, + {file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"}, + {file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"}, + {file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"}, + {file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"}, + {file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"}, + {file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"}, + {file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"}, + {file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"}, + {file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"}, + {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, +] + [[package]] name = "wcwidth" version = "0.2.12" diff --git a/pyproject.toml b/pyproject.toml index 6f29b84..7231630 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,15 +32,20 @@ dev = [ "black>=23.12.1", "commitizen>=3.13.0", "coverage[toml]>=7.4.0", - "fastapi>=0.108.0", + "fastapi>=0.109.2", "litestar>=2.5.0", "mypy>=1.8.0", + "pydantic-settings>=2.1.0", "pytest>=7.4.4", "ruff>=0.1.11", "strawberry-graphql>=0.217.1", "trio>=0.24.0", "uvicorn>=0.25.0", ] +docs = [ + "mkdocs>=1.5.3", + "mkdocs-material>=9.5.9", +] [tool.pdm.build] includes = ["aioinject"] @@ -121,6 +126,12 @@ line-length = 79 [tool.ruff.per-file-ignores] "tests/*" = ["S101"] +"docs/*" = [ + "INP001", # Implicit package (no __init__.py) + "A002", # shadows built-int + "S101", # assert + "T201", # print +] [tool.ruff.mccabe] max-complexity = 6