-
-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathresources.py
More file actions
127 lines (99 loc) · 4.1 KB
/
Copy pathresources.py
File metadata and controls
127 lines (99 loc) · 4.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import typing
from typing_extensions import override
from that_depends.entities.resource_context import ResourceContext
from that_depends.providers.base import AbstractResource, ResourceCreatorType
from that_depends.providers.mixin import SupportsTeardown
T_co = typing.TypeVar("T_co", covariant=True)
P = typing.ParamSpec("P")
class Resource(SupportsTeardown, AbstractResource[T_co]):
"""Provides a resource that is resolved once and cached for future usage.
Unlike a singleton, this provider includes finalization logic and can be
used with a generator or async generator to manage resource lifecycle.
It also supports usage with `typing.ContextManager` or `typing.AsyncContextManager`.
Threading and asyncio concurrency are supported, ensuring only one instance
is created regardless of concurrent resolves.
Example:
```python
async def create_async_resource():
try:
yield "async resource"
finally:
# Finalize resource
pass
class MyContainer:
async_resource = Resource(create_async_resource)
async def main():
async_resource_instance = await MyContainer.async_resource.resolve()
await MyContainer.async_resource.tear_down()
```
"""
__slots__ = (
"_args",
"_context",
"_creator",
"_creator",
"_is_async",
"_kwargs",
"_override",
)
def __init__(
self,
creator: ResourceCreatorType[P, T_co],
*args: P.args,
**kwargs: P.kwargs,
) -> None:
"""Initialize the Resource provider with a callable for resource creation.
The callable can be a generator or async generator that yields the resource
(with optional teardown logic), or a context manager. Only one instance will be
created and cached until explicitly torn down.
Args:
creator: The callable, generator, or context manager that creates the resource.
*args: Positional arguments passed to the creator.
**kwargs: Keyword arguments passed to the creator.
Example:
```python
def custom_creator(name: str):
try:
yield f"Resource created for {name}"
finally:
pass # Teardown
resource_provider = Resource(custom_creator, "example")
instance = resource_provider.sync_resolve()
resource_provider.tear_down()
```
"""
super().__init__(creator, *args, **kwargs)
self._context: typing.Final[ResourceContext[T_co]] = ResourceContext(is_async=self.is_async)
def _fetch_context(self) -> ResourceContext[T_co]:
return self._context
@override
async def tear_down(self, propagate: bool = True) -> None:
"""Tear down the resource if it has been created.
If the resource was never resolved, or was already torn down,
calling this method has no effect.
Example:
```python
# Assuming my_provider was previously resolved
await my_provider.tear_down()
```
"""
await self._fetch_context().tear_down()
self._deregister_arguments()
if propagate:
await self._tear_down_children()
@override
def tear_down_sync(self, propagate: bool = True, raise_on_async: bool = True) -> None:
"""Sync tear down the resource if it has been created.
If the resource was never resolved, or was already torn down,
calling this method has no effect.
If you try to sync tear down an async resource, this will raise an exception.
Example:
```python
# Assuming my_provider was previously resolved
my_provider.sync_tear_down()
```
"""
self._fetch_context().tear_down_sync(propagate=propagate, raise_on_async=raise_on_async)
self._deregister_arguments()
if propagate:
self._tear_down_children_sync(propagate=propagate, raise_on_async=raise_on_async)