diff --git a/docs/index.md b/docs/index.md index 3268e593..933f3488 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,13 +16,12 @@ :maxdepth: 1 :caption: Providers - providers/resource - providers/async-resource + providers/resources + providers/context-resources providers/singleton - providers/factory + providers/factories providers/async-factory - providers/list - providers/dict + providers/collections providers/selector .. toctree:: diff --git a/docs/providers/async-factory.md b/docs/providers/async-factory.md deleted file mode 100644 index a2c59895..00000000 --- a/docs/providers/async-factory.md +++ /dev/null @@ -1,16 +0,0 @@ -# AsyncFactory -- Initialized on every call, as `Factory`. -- Async function is required. -```python -import datetime - -from that_depends import BaseContainer, providers - - -async def async_factory() -> datetime.datetime: - return datetime.datetime.now(tz=datetime.timezone.utc) - - -class DIContainer(BaseContainer): - async_factory = providers.Factory(async_factory) -``` diff --git a/docs/providers/async-resource.md b/docs/providers/async-resource.md deleted file mode 100644 index d2fdba50..00000000 --- a/docs/providers/async-resource.md +++ /dev/null @@ -1,17 +0,0 @@ -# AsyncResource -- Same as `Resource` but async generator function is required. -```python -import typing - -from that_depends import BaseContainer, providers - - -async def create_async_resource() -> typing.AsyncIterator[str]: - # resource initialization - yield "async resource" - # resource teardown - - -class DIContainer(BaseContainer): - async_resource = providers.AsyncResource(create_async_resource) -``` diff --git a/docs/providers/collections.md b/docs/providers/collections.md new file mode 100644 index 00000000..2292794c --- /dev/null +++ b/docs/providers/collections.md @@ -0,0 +1,38 @@ +# Collections +There are several collection providers: `List` and `Dict` + +## List +- List provider contains other providers. +- Resolves into list of dependencies. + +```python +import random +from that_depends import BaseContainer, providers + + +class DIContainer(BaseContainer): + random_number = providers.Factory(random.random) + numbers_sequence = providers.List(random_number, random_number) + + +DIContainer.numbers_sequence.sync_resolve() +# [0.3035656170071561, 0.8280498192037787] +``` + +## Dict +- Dict provider is a collection of named providers. +- Resolves into dict of dependencies. + +```python +import random +from that_depends import BaseContainer, providers + + +class DIContainer(BaseContainer): + random_number = providers.Factory(random.random) + numbers_map = providers.Dict(key1=random_number, key2=random_number) + + +DIContainer.numbers_map.sync_resolve() +# {'key1': 0.6851384528299208, 'key2': 0.41044920948045294} +``` diff --git a/docs/providers/context-resources.md b/docs/providers/context-resources.md new file mode 100644 index 00000000..bb737009 --- /dev/null +++ b/docs/providers/context-resources.md @@ -0,0 +1,57 @@ +# Context resources +Context resources are resources with scoped lifecycle. +There are `ContextResource` and `AsyncContextResource` + +## ContextResource +- Generator function is required. +```python +import typing + +import pytest + +from that_depends import BaseContainer, providers, container_context + + +def create_sync_resource() -> typing.Iterator[str]: + # resource initialization + try: + yield "sync resource" + finally: + pass # resource teardown + +class DIContainer(BaseContainer): + context_resource = providers.ContextResource(create_sync_resource) + + +async def main() -> None: + async with container_context(): + # context resource can be resolved only inside of context + DIContainer.context_resource.sync_resolve() + + # outside the context resolving will fail + with pytest.raises(RuntimeError, match="Context is not set. Use container_context"): + DIContainer.context_resource.sync_resolve() +``` + +## AsyncContextResource +- Async generator function is required. +```python +import typing + +from that_depends import BaseContainer, providers + + +async def create_async_resource() -> typing.AsyncIterator[str]: + # resource initialization + try: + yield "async resource" + finally: + pass # resource teardown + + +class DIContainer(BaseContainer): + async_resource = providers.AsyncContextResource(create_async_resource) + + +# resolving is the same as for ContextResource +``` diff --git a/docs/providers/dict.md b/docs/providers/dict.md deleted file mode 100644 index ab44855a..00000000 --- a/docs/providers/dict.md +++ /dev/null @@ -1,56 +0,0 @@ -# Dict -- It allows you to logically group related dependencies together in a dictionary, making it easier to manage and inject them as a single unit. -- It simplifies the injection process when multiple dependencies need to be passed to a component that expects a dictionary of dependencies. - -## Example Usage -### Step 1: Define the Dependencies -```python -import dataclasses -import typing - -@dataclasses.dataclass(kw_only=True, slots=True) -class ModuleA: - dependency: str - - -@dataclasses.dataclass(kw_only=True, slots=True) -class ModuleB: - dependency: str - - -@dataclasses.dataclass(kw_only=True, slots=True) -class Dispatcher: - modules: typing.Dict[str, typing.Any] - -``` -### Step 2: Define Providers -Next, define the providers for these dependencies using `Factory` and `DictProvider`. - -```python -from that_depends import BaseContainer, providers - - -class DIContainer(BaseContainer): - module_a_provider = providers.Factory(ModuleA, dependency="some_dependency_a") - module_b_provider = providers.Factory(ModuleB, dependency="some_dependency_b") - modules_provider = providers.Dict(module1=module_a_provider, module2=module_b_provider) - dispatcher_provider = providers.Factory(Dispatcher, modules=modules_provider) -``` - -### Step 3: Resolve the Dispatcher -```python -dispatcher = DIContainer.dispatcher_provider.sync_resolve() - -print(dispatcher.modules["module1"].dependency) # Output: some_dependency_a -print(dispatcher.modules["module2"].dependency) # Output: some_dependency_b - -# asynchronous usage example -import asyncio - -async def main(): - dispatcher_async = await container.dispatcher_provider.async_resolve() - print(dispatcher_async.modules["module1"].dependency) # Output: real_dependency_a - print(dispatcher_async.modules["module2"].dependency) # Output: real_dependency_b - -asyncio.run(main()) -``` diff --git a/docs/providers/factories.md b/docs/providers/factories.md new file mode 100644 index 00000000..f2736360 --- /dev/null +++ b/docs/providers/factories.md @@ -0,0 +1,36 @@ +# Factories +Factories are initialized on every call. + +## Factory +- Class or simple function is allowed. +```python +import dataclasses + +from that_depends import BaseContainer, providers + + +@dataclasses.dataclass(kw_only=True, slots=True) +class IndependentFactory: + dep1: str + dep2: int + + +class DIContainer(BaseContainer): + independent_factory = providers.Factory(IndependentFactory, dep1="text", dep2=123) +``` + +## AsyncFactory +- Async function is required. +```python +import datetime + +from that_depends import BaseContainer, providers + + +async def async_factory() -> datetime.datetime: + return datetime.datetime.now(tz=datetime.timezone.utc) + + +class DIContainer(BaseContainer): + async_factory = providers.Factory(async_factory) +``` diff --git a/docs/providers/factory.md b/docs/providers/factory.md deleted file mode 100644 index 43ca140c..00000000 --- a/docs/providers/factory.md +++ /dev/null @@ -1,18 +0,0 @@ -# Factory -- Initialized on every call. -- Class or simple function is allowed. -```python -import dataclasses - -from that_depends import BaseContainer, providers - - -@dataclasses.dataclass(kw_only=True, slots=True) -class IndependentFactory: - dep1: str - dep2: int - - -class DIContainer(BaseContainer): - independent_factory = providers.Factory(IndependentFactory, dep1="text", dep2=123) -``` diff --git a/docs/providers/list.md b/docs/providers/list.md deleted file mode 100644 index bbb15e8b..00000000 --- a/docs/providers/list.md +++ /dev/null @@ -1,13 +0,0 @@ -# List -- List provider contains other providers. -- Resolves into list of dependencies. - -```python -import random -from that_depends import BaseContainer, providers - - -class DIContainer(BaseContainer): - random_number = providers.Factory(random.random) - numbers_sequence = providers.List(random_number, random_number) -``` diff --git a/docs/providers/resource.md b/docs/providers/resource.md deleted file mode 100644 index 0b1a70f0..00000000 --- a/docs/providers/resource.md +++ /dev/null @@ -1,17 +0,0 @@ -# Resource -- Resource initialized only once and have teardown logic. -- Generator function is required. -```python -import typing - -from that_depends import BaseContainer, providers - - -def create_sync_resource() -> typing.Iterator[str]: - # resource initialization - yield "sync resource" - # resource teardown - -class DIContainer(BaseContainer): - sync_resource = providers.Resource(create_sync_resource) -``` diff --git a/docs/providers/resources.md b/docs/providers/resources.md new file mode 100644 index 00000000..2360eaa3 --- /dev/null +++ b/docs/providers/resources.md @@ -0,0 +1,42 @@ +# Resources +Resources are initialized only once and have teardown logic. +There are `Resource` and `AsyncResource` + +## Resource +- Generator function is required. +```python +import typing + +from that_depends import BaseContainer, providers + + +def create_sync_resource() -> typing.Iterator[str]: + # resource initialization + try: + yield "sync resource" + finally: + pass # resource teardown + +class DIContainer(BaseContainer): + sync_resource = providers.Resource(create_sync_resource) +``` + +## AsyncResource +- Async generator function is required. +```python +import typing + +from that_depends import BaseContainer, providers + + +async def create_async_resource() -> typing.AsyncIterator[str]: + # resource initialization + try: + yield "async resource" + finally: + pass # resource teardown + + +class DIContainer(BaseContainer): + async_resource = providers.AsyncResource(create_async_resource) +```