mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-06-17 03:53:14 +03:00
Merge 15c0b00751
into 8460465b5e
This commit is contained in:
commit
06ea25937a
|
@ -210,6 +210,72 @@ first argument.
|
||||||
|
|
||||||
.. _resource-provider-wiring-closing:
|
.. _resource-provider-wiring-closing:
|
||||||
|
|
||||||
|
Scoping Resources using specialized subclasses
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
You can use specialized subclasses of ``Resource`` provider to initialize and shutdown resources by type.
|
||||||
|
Allowing for example to only initialize a subgroup of resources.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
class ScopedResource(resources.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def init_service(name) -> Service:
|
||||||
|
print(f"Init {name}")
|
||||||
|
yield Service()
|
||||||
|
print(f"Shutdown {name}")
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
|
||||||
|
scoped = ScopedResource(
|
||||||
|
init_service,
|
||||||
|
"scoped",
|
||||||
|
)
|
||||||
|
|
||||||
|
generic = providers.Resource(
|
||||||
|
init_service,
|
||||||
|
"generic",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
To initialize resources by type you can use ``init_resources(resource_type)`` and ``shutdown_resources(resource_type)``
|
||||||
|
methods adding the resource type as an argument:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def main():
|
||||||
|
container = Container()
|
||||||
|
container.init_resources(ScopedResource)
|
||||||
|
# Generates:
|
||||||
|
# >>> Init scoped
|
||||||
|
|
||||||
|
container.shutdown_resources(ScopedResource)
|
||||||
|
# Generates:
|
||||||
|
# >>> Shutdown scoped
|
||||||
|
|
||||||
|
|
||||||
|
And to initialize all resources you can use ``init_resources()`` and ``shutdown_resources()`` without arguments:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def main():
|
||||||
|
container = Container()
|
||||||
|
container.init_resources()
|
||||||
|
# Generates:
|
||||||
|
# >>> Init scoped
|
||||||
|
# >>> Init generic
|
||||||
|
|
||||||
|
container.shutdown_resources()
|
||||||
|
# Generates:
|
||||||
|
# >>> Shutdown scoped
|
||||||
|
# >>> Shutdown generic
|
||||||
|
|
||||||
|
|
||||||
|
It works using the :ref:`traverse` method to find all resources of the specified type, selecting all resources
|
||||||
|
which are instances of the specified type.
|
||||||
|
|
||||||
|
|
||||||
Resources, wiring, and per-function execution scope
|
Resources, wiring, and per-function execution scope
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ from typing import (
|
||||||
overload,
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .providers import Provider, Self, ProviderParent
|
from .providers import Provider, Resource, Self, ProviderParent
|
||||||
|
|
||||||
C_Base = TypeVar("C_Base", bound="Container")
|
C_Base = TypeVar("C_Base", bound="Container")
|
||||||
C = TypeVar("C", bound="DeclarativeContainer")
|
C = TypeVar("C", bound="DeclarativeContainer")
|
||||||
|
@ -67,8 +67,8 @@ class Container:
|
||||||
from_package: Optional[str] = None,
|
from_package: Optional[str] = None,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def unwire(self) -> None: ...
|
def unwire(self) -> None: ...
|
||||||
def init_resources(self) -> Optional[Awaitable]: ...
|
def init_resources(self, resource_type: Type[Resource]=None) -> Optional[Awaitable]: ...
|
||||||
def shutdown_resources(self) -> Optional[Awaitable]: ...
|
def shutdown_resources(self, resource_type: Type[Resource]=None) -> Optional[Awaitable]: ...
|
||||||
def load_config(self) -> None: ...
|
def load_config(self) -> None: ...
|
||||||
def apply_container_providers_overridings(self) -> None: ...
|
def apply_container_providers_overridings(self) -> None: ...
|
||||||
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
|
def reset_singletons(self) -> SingletonResetContext[C_Base]: ...
|
||||||
|
|
|
@ -310,11 +310,11 @@ class DynamicContainer(Container):
|
||||||
self.wired_to_modules.clear()
|
self.wired_to_modules.clear()
|
||||||
self.wired_to_packages.clear()
|
self.wired_to_packages.clear()
|
||||||
|
|
||||||
def init_resources(self):
|
def init_resources(self, resource_type=providers.Resource):
|
||||||
"""Initialize all container resources."""
|
"""Initialize all container resources."""
|
||||||
futures = []
|
futures = []
|
||||||
|
|
||||||
for provider in self.traverse(types=[providers.Resource]):
|
for provider in self.traverse(types=[resource_type]):
|
||||||
resource = provider.init()
|
resource = provider.init()
|
||||||
|
|
||||||
if __is_future_or_coroutine(resource):
|
if __is_future_or_coroutine(resource):
|
||||||
|
@ -323,7 +323,7 @@ class DynamicContainer(Container):
|
||||||
if futures:
|
if futures:
|
||||||
return asyncio.gather(*futures)
|
return asyncio.gather(*futures)
|
||||||
|
|
||||||
def shutdown_resources(self):
|
def shutdown_resources(self, resource_type=providers.Resource):
|
||||||
"""Shutdown all container resources."""
|
"""Shutdown all container resources."""
|
||||||
def _independent_resources(resources):
|
def _independent_resources(resources):
|
||||||
for resource in resources:
|
for resource in resources:
|
||||||
|
@ -355,7 +355,7 @@ class DynamicContainer(Container):
|
||||||
for resource in resources_to_shutdown:
|
for resource in resources_to_shutdown:
|
||||||
resource.shutdown()
|
resource.shutdown()
|
||||||
|
|
||||||
resources = list(self.traverse(types=[providers.Resource]))
|
resources = list(self.traverse(types=[resource_type]))
|
||||||
if any(resource.is_async_mode_enabled() for resource in resources):
|
if any(resource.is_async_mode_enabled() for resource in resources):
|
||||||
return _async_ordered_shutdown(resources)
|
return _async_ordered_shutdown(resources)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -145,3 +145,121 @@ async def test_shutdown_sync_and_async_ordering():
|
||||||
await container.shutdown_resources()
|
await container.shutdown_resources()
|
||||||
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
||||||
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]
|
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]
|
||||||
|
|
||||||
|
|
||||||
|
@mark.asyncio
|
||||||
|
async def test_init_and_shutdown_scoped_resources():
|
||||||
|
initialized_resources = []
|
||||||
|
shutdown_resources = []
|
||||||
|
|
||||||
|
def _sync_resource(name, **_):
|
||||||
|
initialized_resources.append(name)
|
||||||
|
yield name
|
||||||
|
shutdown_resources.append(name)
|
||||||
|
|
||||||
|
async def _async_resource(name, **_):
|
||||||
|
initialized_resources.append(name)
|
||||||
|
yield name
|
||||||
|
shutdown_resources.append(name)
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceA(providers.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceB(providers.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
resource_a = ResourceA(
|
||||||
|
_sync_resource,
|
||||||
|
name="ra1",
|
||||||
|
)
|
||||||
|
resource_b1 = ResourceB(
|
||||||
|
_sync_resource,
|
||||||
|
name="rb1",
|
||||||
|
r1=resource_a,
|
||||||
|
)
|
||||||
|
resource_b2 = ResourceB(
|
||||||
|
_async_resource,
|
||||||
|
name="rb2",
|
||||||
|
r2=resource_b1,
|
||||||
|
)
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
container.init_resources(resource_type=ResourceA)
|
||||||
|
assert initialized_resources == ["ra1"]
|
||||||
|
assert shutdown_resources == []
|
||||||
|
|
||||||
|
container.shutdown_resources(resource_type=ResourceA)
|
||||||
|
assert initialized_resources == ["ra1"]
|
||||||
|
assert shutdown_resources == ["ra1"]
|
||||||
|
|
||||||
|
await container.init_resources(resource_type=ResourceB)
|
||||||
|
assert initialized_resources == ["ra1", "ra1", "rb1", "rb2"]
|
||||||
|
assert shutdown_resources == ["ra1"]
|
||||||
|
|
||||||
|
await container.shutdown_resources(resource_type=ResourceB)
|
||||||
|
assert initialized_resources == ["ra1", "ra1", "rb1", "rb2"]
|
||||||
|
assert shutdown_resources == ["ra1", "rb2", "rb1"]
|
||||||
|
|
||||||
|
|
||||||
|
@mark.asyncio
|
||||||
|
async def test_init_and_shutdown_all_scoped_resources_using_default_value():
|
||||||
|
initialized_resources = []
|
||||||
|
shutdown_resources = []
|
||||||
|
|
||||||
|
def _sync_resource(name, **_):
|
||||||
|
initialized_resources.append(name)
|
||||||
|
yield name
|
||||||
|
shutdown_resources.append(name)
|
||||||
|
|
||||||
|
async def _async_resource(name, **_):
|
||||||
|
initialized_resources.append(name)
|
||||||
|
yield name
|
||||||
|
shutdown_resources.append(name)
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceA(providers.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceB(providers.Resource):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Container(containers.DeclarativeContainer):
|
||||||
|
resource_a = ResourceA(
|
||||||
|
_sync_resource,
|
||||||
|
name="r1",
|
||||||
|
)
|
||||||
|
resource_b1 = ResourceB(
|
||||||
|
_sync_resource,
|
||||||
|
name="r2",
|
||||||
|
r1=resource_a,
|
||||||
|
)
|
||||||
|
resource_b2 = ResourceB(
|
||||||
|
_async_resource,
|
||||||
|
name="r3",
|
||||||
|
r2=resource_b1,
|
||||||
|
)
|
||||||
|
|
||||||
|
container = Container()
|
||||||
|
|
||||||
|
await container.init_resources()
|
||||||
|
assert initialized_resources == ["r1", "r2", "r3"]
|
||||||
|
assert shutdown_resources == []
|
||||||
|
|
||||||
|
await container.shutdown_resources()
|
||||||
|
assert initialized_resources == ["r1", "r2", "r3"]
|
||||||
|
assert shutdown_resources == ["r3", "r2", "r1"]
|
||||||
|
|
||||||
|
await container.init_resources()
|
||||||
|
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
||||||
|
assert shutdown_resources == ["r3", "r2", "r1"]
|
||||||
|
|
||||||
|
await container.shutdown_resources()
|
||||||
|
assert initialized_resources == ["r1", "r2", "r3", "r1", "r2", "r3"]
|
||||||
|
assert shutdown_resources == ["r3", "r2", "r1", "r3", "r2", "r1"]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user