asyncstdlib 3.12.1__tar.gz → 3.12.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/PKG-INFO +2 -1
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/__init__.py +9 -2
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/_lrucache.py +29 -16
- asyncstdlib-3.12.2/asyncstdlib/_lrucache.pyi +90 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/_typing.py +16 -3
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/asynctools.py +6 -5
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/builtins.py +1 -1
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/contextlib.py +64 -41
- asyncstdlib-3.12.2/asyncstdlib/contextlib.pyi +115 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/functools.py +12 -15
- asyncstdlib-3.12.2/asyncstdlib/functools.pyi +26 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/heapq.py +9 -31
- asyncstdlib-3.12.2/asyncstdlib/heapq.pyi +40 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/itertools.py +13 -135
- asyncstdlib-3.12.2/asyncstdlib/itertools.pyi +238 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/pyproject.toml +1 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/test_contextlib.py +19 -0
- asyncstdlib-3.12.1/asyncstdlib/_lrucache.pyi +0 -61
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/LICENSE +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/README.rst +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/_core.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/_utility.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/builtins.pyi +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/asyncstdlib/py.typed +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/__init__.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/test_asynctools.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/test_builtins.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/test_functools.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/test_functools_lru.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/test_heapq.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/test_helpers.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/test_itertools.py +0 -0
- {asyncstdlib-3.12.1 → asyncstdlib-3.12.2}/unittests/utility.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: asyncstdlib
|
|
3
|
-
Version: 3.12.
|
|
3
|
+
Version: 3.12.2
|
|
4
4
|
Summary: The missing async toolbox
|
|
5
5
|
Keywords: async,enumerate,itertools,builtins,functools,contextlib
|
|
6
6
|
Author-email: Max Fischer <maxfischer2781@gmail.com>
|
|
@@ -26,6 +26,7 @@ Requires-Dist: coverage ; extra == "test"
|
|
|
26
26
|
Requires-Dist: pytest-cov ; extra == "test"
|
|
27
27
|
Requires-Dist: flake8-2020 ; extra == "test"
|
|
28
28
|
Requires-Dist: mypy ; extra == "test" and ( implementation_name=='cpython')
|
|
29
|
+
Requires-Dist: typing-extensions ; extra == "test"
|
|
29
30
|
Project-URL: Documentation, https://asyncstdlib.readthedocs.io/en/latest/
|
|
30
31
|
Project-URL: Source, https://github.com/maxfischer2781/asyncstdlib
|
|
31
32
|
Provides-Extra: doc
|
|
@@ -19,7 +19,13 @@ from .builtins import (
|
|
|
19
19
|
sorted,
|
|
20
20
|
)
|
|
21
21
|
from .functools import reduce, lru_cache, cache, cached_property
|
|
22
|
-
from .contextlib import
|
|
22
|
+
from .contextlib import (
|
|
23
|
+
closing,
|
|
24
|
+
ContextDecorator,
|
|
25
|
+
contextmanager,
|
|
26
|
+
nullcontext,
|
|
27
|
+
ExitStack,
|
|
28
|
+
)
|
|
23
29
|
from .itertools import (
|
|
24
30
|
accumulate,
|
|
25
31
|
batched,
|
|
@@ -39,7 +45,7 @@ from .itertools import (
|
|
|
39
45
|
from .asynctools import borrow, scoped_iter, await_each, any_iter, apply, sync
|
|
40
46
|
from .heapq import merge, nlargest, nsmallest
|
|
41
47
|
|
|
42
|
-
__version__ = "3.12.
|
|
48
|
+
__version__ = "3.12.2"
|
|
43
49
|
|
|
44
50
|
__all__ = [
|
|
45
51
|
"anext",
|
|
@@ -65,6 +71,7 @@ __all__ = [
|
|
|
65
71
|
"cached_property",
|
|
66
72
|
# contextlib
|
|
67
73
|
"closing",
|
|
74
|
+
"ContextDecorator",
|
|
68
75
|
"contextmanager",
|
|
69
76
|
"nullcontext",
|
|
70
77
|
"ExitStack",
|
|
@@ -6,7 +6,10 @@ several performance hacks are skipped in favour of maintainability,
|
|
|
6
6
|
especially when they might not apply to PyPy.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
from __future__ import annotations
|
|
9
10
|
from typing import (
|
|
11
|
+
Generic,
|
|
12
|
+
TypeVar,
|
|
10
13
|
NamedTuple,
|
|
11
14
|
Callable,
|
|
12
15
|
Any,
|
|
@@ -72,6 +75,14 @@ class LRUAsyncCallable(Protocol[AC]):
|
|
|
72
75
|
"""The callable wrapped by this cache"""
|
|
73
76
|
raise NotImplementedError
|
|
74
77
|
|
|
78
|
+
def __get__(
|
|
79
|
+
self: LRUAsyncCallable[Any], instance: object, owner: Optional[type] = None
|
|
80
|
+
) -> Any:
|
|
81
|
+
"""Descriptor ``__get__`` for caches to bind them on lookup"""
|
|
82
|
+
if instance is None:
|
|
83
|
+
return self
|
|
84
|
+
return LRUAsyncBoundCallable(self, instance)
|
|
85
|
+
|
|
75
86
|
#: Get the result of ``await __wrapped__(...)`` from the cache or evaluation
|
|
76
87
|
__call__: AC
|
|
77
88
|
|
|
@@ -106,23 +117,37 @@ class LRUAsyncCallable(Protocol[AC]):
|
|
|
106
117
|
...
|
|
107
118
|
|
|
108
119
|
|
|
109
|
-
|
|
120
|
+
# these are fake and only exist for placeholders
|
|
121
|
+
S = TypeVar("S")
|
|
122
|
+
S2 = TypeVar("S2")
|
|
123
|
+
P = TypeVar("P")
|
|
124
|
+
R = TypeVar("R")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class LRUAsyncBoundCallable(Generic[S, P, R]): # type: ignore[reportInvalidTypeVarUse]
|
|
110
128
|
"""A :py:class:`~.LRUAsyncCallable` that is bound like a method"""
|
|
111
129
|
|
|
112
130
|
__slots__ = ("__weakref__", "_lru", "__self__")
|
|
113
131
|
|
|
114
|
-
def __init__(self, lru: LRUAsyncCallable[
|
|
132
|
+
def __init__(self, lru: LRUAsyncCallable[Any], __self__: object):
|
|
115
133
|
self._lru = lru
|
|
116
134
|
self.__self__ = __self__
|
|
117
135
|
|
|
118
136
|
@property
|
|
119
|
-
def __wrapped__(self) ->
|
|
137
|
+
def __wrapped__(self) -> Any:
|
|
120
138
|
return self._lru.__wrapped__
|
|
121
139
|
|
|
122
140
|
@property
|
|
123
|
-
def __func__(self) -> LRUAsyncCallable[
|
|
141
|
+
def __func__(self) -> LRUAsyncCallable[Any]:
|
|
124
142
|
return self._lru
|
|
125
143
|
|
|
144
|
+
def __get__(
|
|
145
|
+
self: LRUAsyncBoundCallable[S, P, R],
|
|
146
|
+
instance: S2,
|
|
147
|
+
owner: Optional[type] = None,
|
|
148
|
+
) -> LRUAsyncBoundCallable[S2, P, R]:
|
|
149
|
+
return LRUAsyncBoundCallable(self._lru, instance)
|
|
150
|
+
|
|
126
151
|
def __call__(self, *args, **kwargs): # type: ignore
|
|
127
152
|
return self._lru(self.__self__, *args, **kwargs)
|
|
128
153
|
|
|
@@ -289,22 +314,12 @@ class CallKey:
|
|
|
289
314
|
return cls(key)
|
|
290
315
|
|
|
291
316
|
|
|
292
|
-
def cache__get(
|
|
293
|
-
self: LRUAsyncCallable[AC], instance: object, owner: Optional[type] = None
|
|
294
|
-
) -> LRUAsyncCallable[AC]:
|
|
295
|
-
"""Descriptor ``__get__`` for caches to bind them on lookup"""
|
|
296
|
-
if instance is None:
|
|
297
|
-
return self
|
|
298
|
-
return LRUAsyncBoundCallable(self, instance)
|
|
299
|
-
|
|
300
|
-
|
|
301
317
|
class UncachedLRUAsyncCallable(LRUAsyncCallable[AC]):
|
|
302
318
|
"""Wrap the async ``call`` to track accesses as for caching/memoization"""
|
|
303
319
|
|
|
304
320
|
__slots__ = ("__weakref__", "__dict__", "__wrapped__", "__misses", "__typed")
|
|
305
321
|
|
|
306
322
|
__wrapped__: AC
|
|
307
|
-
__get__ = cache__get
|
|
308
323
|
|
|
309
324
|
def __init__(self, call: AC, typed: bool):
|
|
310
325
|
self.__wrapped__ = call # type: ignore[reportIncompatibleMethodOverride]
|
|
@@ -342,7 +357,6 @@ class MemoizedLRUAsyncCallable(LRUAsyncCallable[AC]):
|
|
|
342
357
|
)
|
|
343
358
|
|
|
344
359
|
__wrapped__: AC
|
|
345
|
-
__get__ = cache__get
|
|
346
360
|
|
|
347
361
|
def __init__(self, call: AC, typed: bool):
|
|
348
362
|
self.__wrapped__ = call # type: ignore[reportIncompatibleMethodOverride]
|
|
@@ -397,7 +411,6 @@ class CachedLRUAsyncCallable(LRUAsyncCallable[AC]):
|
|
|
397
411
|
)
|
|
398
412
|
|
|
399
413
|
__wrapped__: AC
|
|
400
|
-
__get__ = cache__get
|
|
401
414
|
|
|
402
415
|
def __init__(self, call: AC, typed: bool, maxsize: int):
|
|
403
416
|
self.__wrapped__ = call # type: ignore[reportIncompatibleMethodOverride]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
TypeVar,
|
|
3
|
+
Any,
|
|
4
|
+
Awaitable,
|
|
5
|
+
Callable,
|
|
6
|
+
Coroutine,
|
|
7
|
+
Generic,
|
|
8
|
+
NamedTuple,
|
|
9
|
+
overload,
|
|
10
|
+
Protocol,
|
|
11
|
+
)
|
|
12
|
+
from typing_extensions import ParamSpec, Concatenate
|
|
13
|
+
|
|
14
|
+
from ._typing import AC, TypedDict
|
|
15
|
+
|
|
16
|
+
class CacheInfo(NamedTuple):
|
|
17
|
+
hits: int
|
|
18
|
+
misses: int
|
|
19
|
+
maxsize: int | None
|
|
20
|
+
currsize: int
|
|
21
|
+
|
|
22
|
+
class CacheParameters(TypedDict):
|
|
23
|
+
maxsize: int | None
|
|
24
|
+
typed: bool
|
|
25
|
+
|
|
26
|
+
R = TypeVar("R")
|
|
27
|
+
P = ParamSpec("P")
|
|
28
|
+
S = TypeVar("S")
|
|
29
|
+
S2 = TypeVar("S2")
|
|
30
|
+
|
|
31
|
+
class LRUAsyncCallable(Protocol[AC]):
|
|
32
|
+
__slots__: tuple[str, ...]
|
|
33
|
+
__call__: AC
|
|
34
|
+
@overload
|
|
35
|
+
def __get__(
|
|
36
|
+
self: LRUAsyncCallable[AC], instance: None, owner: type | None = ...
|
|
37
|
+
) -> LRUAsyncCallable[AC]: ...
|
|
38
|
+
@overload
|
|
39
|
+
def __get__(
|
|
40
|
+
self: LRUAsyncCallable[Callable[Concatenate[S, P], Coroutine[Any, Any, R]]],
|
|
41
|
+
instance: S,
|
|
42
|
+
owner: type | None = ...,
|
|
43
|
+
) -> LRUAsyncBoundCallable[S, P, R]: ...
|
|
44
|
+
@overload
|
|
45
|
+
def __get__(
|
|
46
|
+
self: LRUAsyncCallable[Callable[Concatenate[S, P], Awaitable[R]]],
|
|
47
|
+
instance: S,
|
|
48
|
+
owner: type | None = ...,
|
|
49
|
+
) -> LRUAsyncBoundCallable[S, P, R]: ...
|
|
50
|
+
@property
|
|
51
|
+
def __wrapped__(self) -> AC: ...
|
|
52
|
+
def cache_parameters(self) -> CacheParameters: ...
|
|
53
|
+
def cache_info(self) -> CacheInfo: ...
|
|
54
|
+
def cache_clear(self) -> None: ...
|
|
55
|
+
def cache_discard(self, *args: Any, **kwargs: Any) -> None: ...
|
|
56
|
+
|
|
57
|
+
class LRUAsyncBoundCallable(Generic[S, P, R]):
|
|
58
|
+
__slots__: tuple[str, ...]
|
|
59
|
+
__self__: S
|
|
60
|
+
__call__: Callable[P, Awaitable[R]]
|
|
61
|
+
@overload
|
|
62
|
+
def __get__(
|
|
63
|
+
self, instance: None, owner: type | None = ...
|
|
64
|
+
) -> LRUAsyncBoundCallable[S, P, R]: ...
|
|
65
|
+
@overload
|
|
66
|
+
def __get__(
|
|
67
|
+
self, instance: S2, owner: type | None = ...
|
|
68
|
+
) -> LRUAsyncBoundCallable[S2, P, R]: ...
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
lru: LRUAsyncCallable[Callable[Concatenate[S, P], Awaitable[R]]],
|
|
72
|
+
__self__: S,
|
|
73
|
+
) -> None: ...
|
|
74
|
+
@property
|
|
75
|
+
def __wrapped__(self) -> Callable[Concatenate[S, P], Awaitable[R]]: ...
|
|
76
|
+
@property
|
|
77
|
+
def __func__(
|
|
78
|
+
self,
|
|
79
|
+
) -> LRUAsyncCallable[Callable[Concatenate[S, P], Awaitable[R]]]: ...
|
|
80
|
+
def cache_parameters(self) -> CacheParameters: ...
|
|
81
|
+
def cache_info(self) -> CacheInfo: ...
|
|
82
|
+
def cache_clear(self) -> None: ...
|
|
83
|
+
def cache_discard(self, *args: Any, **kwargs: Any) -> None: ...
|
|
84
|
+
|
|
85
|
+
@overload
|
|
86
|
+
def lru_cache(maxsize: AC, typed: bool = ...) -> LRUAsyncCallable[AC]: ...
|
|
87
|
+
@overload
|
|
88
|
+
def lru_cache(
|
|
89
|
+
maxsize: int | None = ..., typed: bool = ...
|
|
90
|
+
) -> Callable[[AC], LRUAsyncCallable[AC]]: ...
|
|
@@ -14,13 +14,14 @@ from typing import (
|
|
|
14
14
|
Callable,
|
|
15
15
|
Any,
|
|
16
16
|
Awaitable,
|
|
17
|
+
runtime_checkable,
|
|
18
|
+
Protocol,
|
|
19
|
+
ContextManager,
|
|
20
|
+
TypedDict,
|
|
17
21
|
)
|
|
18
22
|
|
|
19
|
-
from typing import Protocol, AsyncContextManager, ContextManager, TypedDict
|
|
20
|
-
|
|
21
23
|
__all__ = [
|
|
22
24
|
"Protocol",
|
|
23
|
-
"AsyncContextManager",
|
|
24
25
|
"ContextManager",
|
|
25
26
|
"TypedDict",
|
|
26
27
|
"T",
|
|
@@ -35,6 +36,8 @@ __all__ = [
|
|
|
35
36
|
"HK",
|
|
36
37
|
"LT",
|
|
37
38
|
"ADD",
|
|
39
|
+
"AClose",
|
|
40
|
+
"ACloseable",
|
|
38
41
|
"AnyIterable",
|
|
39
42
|
]
|
|
40
43
|
|
|
@@ -70,5 +73,15 @@ class SupportsAdd(Protocol):
|
|
|
70
73
|
raise NotImplementedError
|
|
71
74
|
|
|
72
75
|
|
|
76
|
+
# await AClose.aclose()
|
|
77
|
+
AClose = TypeVar("AClose", bound="ACloseable")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@runtime_checkable
|
|
81
|
+
class ACloseable(Protocol):
|
|
82
|
+
async def aclose(self) -> None:
|
|
83
|
+
"""Asynchronously close this object"""
|
|
84
|
+
|
|
85
|
+
|
|
73
86
|
#: (async) iter T
|
|
74
87
|
AnyIterable = Union[Iterable[T], AsyncIterable[T]]
|
|
@@ -2,6 +2,7 @@ from asyncio import iscoroutinefunction
|
|
|
2
2
|
from functools import wraps
|
|
3
3
|
from typing import (
|
|
4
4
|
Union,
|
|
5
|
+
AsyncContextManager,
|
|
5
6
|
AsyncIterator,
|
|
6
7
|
TypeVar,
|
|
7
8
|
AsyncGenerator,
|
|
@@ -14,7 +15,7 @@ from typing import (
|
|
|
14
15
|
Optional,
|
|
15
16
|
)
|
|
16
17
|
|
|
17
|
-
from ._typing import
|
|
18
|
+
from ._typing import T, T1, T2, T3, T4, T5, AnyIterable
|
|
18
19
|
from ._core import aiter
|
|
19
20
|
from .contextlib import nullcontext
|
|
20
21
|
|
|
@@ -50,11 +51,11 @@ class _BorrowedAsyncIterator(AsyncGenerator[T, S]):
|
|
|
50
51
|
self.__anext__ = self._wrapper.__anext__ # type: ignore
|
|
51
52
|
if hasattr(iterator, "asend"):
|
|
52
53
|
self.asend = (
|
|
53
|
-
iterator.asend # pyright: ignore[reportUnknownMemberType,
|
|
54
|
+
iterator.asend # pyright: ignore[reportUnknownMemberType,reportAttributeAccessIssue]
|
|
54
55
|
)
|
|
55
56
|
if hasattr(iterator, "athrow"):
|
|
56
57
|
self.athrow = (
|
|
57
|
-
iterator.athrow # pyright: ignore[reportUnknownMemberType,
|
|
58
|
+
iterator.athrow # pyright: ignore[reportUnknownMemberType,reportAttributeAccessIssue]
|
|
58
59
|
)
|
|
59
60
|
|
|
60
61
|
def __aiter__(self) -> AsyncGenerator[T, S]:
|
|
@@ -409,9 +410,9 @@ async def any_iter(
|
|
|
409
410
|
async for item in iterable:
|
|
410
411
|
yield (
|
|
411
412
|
item if not isinstance(item, Awaitable) else await item
|
|
412
|
-
) # pyright: ignore[
|
|
413
|
+
) # pyright: ignore[reportReturnType]
|
|
413
414
|
else:
|
|
414
415
|
for item in iterable:
|
|
415
416
|
yield (
|
|
416
417
|
item if not isinstance(item, Awaitable) else await item
|
|
417
|
-
) # pyright: ignore[
|
|
418
|
+
) # pyright: ignore[reportReturnType]
|
|
@@ -444,7 +444,7 @@ async def sorted(
|
|
|
444
444
|
try:
|
|
445
445
|
return _sync_builtins.sorted(iterable, reverse=reverse) # type: ignore
|
|
446
446
|
except TypeError:
|
|
447
|
-
items:
|
|
447
|
+
items: _sync_builtins.list[Any] = [item async for item in aiter(iterable)]
|
|
448
448
|
items.sort(reverse=reverse)
|
|
449
449
|
return items
|
|
450
450
|
else:
|
|
@@ -8,18 +8,17 @@ from typing import (
|
|
|
8
8
|
Any,
|
|
9
9
|
Awaitable,
|
|
10
10
|
Deque,
|
|
11
|
-
|
|
11
|
+
AsyncContextManager,
|
|
12
12
|
)
|
|
13
13
|
from functools import wraps
|
|
14
14
|
from collections import deque
|
|
15
15
|
from functools import partial
|
|
16
16
|
import sys
|
|
17
17
|
|
|
18
|
-
from ._typing import
|
|
18
|
+
from ._typing import AClose, ContextManager, AC, T, C
|
|
19
19
|
from ._core import awaitify
|
|
20
20
|
from ._utility import public_module
|
|
21
21
|
|
|
22
|
-
|
|
23
22
|
AnyContextManager = Union[AsyncContextManager[T], ContextManager[T]]
|
|
24
23
|
|
|
25
24
|
|
|
@@ -28,18 +27,10 @@ AnyContextManager = Union[AsyncContextManager[T], ContextManager[T]]
|
|
|
28
27
|
AbstractContextManager = AsyncContextManager
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
class ACloseable(Protocol):
|
|
32
|
-
async def aclose(self) -> None:
|
|
33
|
-
"""Asynchronously close this object"""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
AC = TypeVar("AC", bound=ACloseable)
|
|
37
|
-
|
|
38
|
-
|
|
39
30
|
def contextmanager(
|
|
40
31
|
func: Callable[..., AsyncGenerator[T, None]]
|
|
41
32
|
) -> Callable[..., AsyncContextManager[T]]:
|
|
42
|
-
"""
|
|
33
|
+
r"""
|
|
43
34
|
Create an asynchronous context manager out of an asynchronous generator function
|
|
44
35
|
|
|
45
36
|
This is intended as a decorator for an asynchronous generator function.
|
|
@@ -50,7 +41,7 @@ def contextmanager(
|
|
|
50
41
|
.. code-block:: python3
|
|
51
42
|
|
|
52
43
|
@contextmanager
|
|
53
|
-
async def
|
|
44
|
+
async def context(*args, **kwargs):
|
|
54
45
|
# __aenter__
|
|
55
46
|
yield # context value
|
|
56
47
|
# __aexit__
|
|
@@ -58,6 +49,9 @@ def contextmanager(
|
|
|
58
49
|
Note that if an exception ends the context block, it gets re-raised at the ``yield``
|
|
59
50
|
inside the asynchronous generator (via :py:meth:`~agen.athrow`). In order to handle
|
|
60
51
|
this exception, the ``yield`` should be wrapped in a ``try`` statement.
|
|
52
|
+
|
|
53
|
+
The created context manager is a :py:class:`~.ContextDecorator` and can also be used
|
|
54
|
+
as a decorator. It is automatically entered when a decorated function is ``await``\ ed.
|
|
61
55
|
"""
|
|
62
56
|
|
|
63
57
|
@wraps(func)
|
|
@@ -67,13 +61,62 @@ def contextmanager(
|
|
|
67
61
|
return helper
|
|
68
62
|
|
|
69
63
|
|
|
70
|
-
class
|
|
64
|
+
class ContextDecorator(AsyncContextManager[T]):
|
|
65
|
+
"""
|
|
66
|
+
Base class to turn an async context manager into a decorator as well
|
|
67
|
+
|
|
68
|
+
Inheriting from this class adds the scaffolding to automatically enter
|
|
69
|
+
an async context manager on awaiting any callable decorated with it:
|
|
70
|
+
|
|
71
|
+
.. code:: python3
|
|
72
|
+
|
|
73
|
+
class DecoratorAndContext(ContextDecorator):
|
|
74
|
+
async def __aenter__(self) -> Any:
|
|
75
|
+
print("entering", self)
|
|
76
|
+
|
|
77
|
+
async def __aexit__(self, *exc):
|
|
78
|
+
print("exiting", self)
|
|
79
|
+
|
|
80
|
+
@DecoratorAndContext()
|
|
81
|
+
async def func():
|
|
82
|
+
print("running some function...")
|
|
83
|
+
|
|
84
|
+
The context manager can still be used regularly in `async with` statements.
|
|
85
|
+
|
|
86
|
+
Since functions are decorated with an existing context manager instance,
|
|
87
|
+
the same instance is entered and exited on every call. If the context is
|
|
88
|
+
not safe to be entered multiple times or even concurrently it should implement
|
|
89
|
+
the method ``_recreate_cm(:Self) -> Self`` to create a copy of itself.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
__slots__ = ()
|
|
93
|
+
|
|
94
|
+
def _recreate_cm(self):
|
|
95
|
+
"""Return another instance of the context manager that is ready for entering again"""
|
|
96
|
+
# Default to assuming the CM is reentrant, concurrency safe, ... and just return itself.
|
|
97
|
+
# Since for the `async` case many CMs have to be this anyway, this should apply most.
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def __call__(self, func: AC, /) -> AC:
|
|
101
|
+
@wraps(func)
|
|
102
|
+
async def inner(*args: Any, **kwds: Any) -> Any:
|
|
103
|
+
async with self._recreate_cm():
|
|
104
|
+
return await func(*args, **kwds)
|
|
105
|
+
|
|
106
|
+
return inner # type: ignore
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class _AsyncGeneratorContextManager(ContextDecorator[T]):
|
|
71
110
|
def __init__(
|
|
72
111
|
self, func: Callable[..., AsyncGenerator[T, None]], args: Any, kwds: Any
|
|
73
112
|
):
|
|
74
113
|
self.gen = func(*args, **kwds)
|
|
114
|
+
self.__recreate_args = func, args, kwds
|
|
75
115
|
self.__doc__ = getattr(func, "__doc__", type(self).__doc__)
|
|
76
116
|
|
|
117
|
+
def _recreate_cm(self):
|
|
118
|
+
return type(self)(*self.__recreate_args)
|
|
119
|
+
|
|
77
120
|
async def __aenter__(self) -> T:
|
|
78
121
|
try:
|
|
79
122
|
return await self.gen.__anext__()
|
|
@@ -126,7 +169,7 @@ class _AsyncGeneratorContextManager(Generic[T]):
|
|
|
126
169
|
|
|
127
170
|
|
|
128
171
|
@public_module(__name__, "closing")
|
|
129
|
-
class Closing(Generic[
|
|
172
|
+
class Closing(Generic[AClose]):
|
|
130
173
|
"""
|
|
131
174
|
Create an :term:`asynchronous context manager` to ``aclose`` some ``thing`` on exit
|
|
132
175
|
|
|
@@ -150,10 +193,10 @@ class Closing(Generic[AC]):
|
|
|
150
193
|
is eventually closed and only :term:`borrowed <borrowing>` until then.
|
|
151
194
|
"""
|
|
152
195
|
|
|
153
|
-
def __init__(self, thing:
|
|
196
|
+
def __init__(self, thing: AClose):
|
|
154
197
|
self.thing = thing
|
|
155
198
|
|
|
156
|
-
async def __aenter__(self) ->
|
|
199
|
+
async def __aenter__(self) -> AClose:
|
|
157
200
|
return self.thing
|
|
158
201
|
|
|
159
202
|
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> bool:
|
|
@@ -165,7 +208,7 @@ closing = Closing
|
|
|
165
208
|
|
|
166
209
|
|
|
167
210
|
@public_module(__name__, "nullcontext")
|
|
168
|
-
class NullContext(
|
|
211
|
+
class NullContext(AsyncContextManager[T]):
|
|
169
212
|
"""
|
|
170
213
|
Create an :term:`asynchronous context manager` that only returns ``enter_result``
|
|
171
214
|
|
|
@@ -190,22 +233,10 @@ class NullContext(Generic[T]):
|
|
|
190
233
|
|
|
191
234
|
__slots__ = ("enter_result",)
|
|
192
235
|
|
|
193
|
-
|
|
194
|
-
def __init__(self: "NullContext[None]", enter_result: None = ...) -> None: ...
|
|
195
|
-
|
|
196
|
-
@overload
|
|
197
|
-
def __init__(self: "NullContext[T]", enter_result: T) -> None: ...
|
|
198
|
-
|
|
199
|
-
def __init__(self, enter_result: Optional[T] = None):
|
|
236
|
+
def __init__(self, enter_result: T = None):
|
|
200
237
|
self.enter_result = enter_result
|
|
201
238
|
|
|
202
|
-
|
|
203
|
-
async def __aenter__(self: "NullContext[None]") -> None: ...
|
|
204
|
-
|
|
205
|
-
@overload
|
|
206
|
-
async def __aenter__(self: "NullContext[T]") -> T: ...
|
|
207
|
-
|
|
208
|
-
async def __aenter__(self) -> Optional[T]:
|
|
239
|
+
async def __aenter__(self) -> T:
|
|
209
240
|
return self.enter_result
|
|
210
241
|
|
|
211
242
|
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> bool:
|
|
@@ -215,15 +246,7 @@ class NullContext(Generic[T]):
|
|
|
215
246
|
nullcontext = NullContext
|
|
216
247
|
|
|
217
248
|
|
|
218
|
-
SE = TypeVar(
|
|
219
|
-
"SE",
|
|
220
|
-
bound=Union[
|
|
221
|
-
AsyncContextManager[Any],
|
|
222
|
-
ContextManager[Any],
|
|
223
|
-
Callable[[Any, BaseException, Any], Optional[bool]],
|
|
224
|
-
Callable[[Any, BaseException, Any], Awaitable[Optional[bool]]],
|
|
225
|
-
],
|
|
226
|
-
)
|
|
249
|
+
SE = TypeVar("SE")
|
|
227
250
|
|
|
228
251
|
|
|
229
252
|
class ExitStack:
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
TypeVar,
|
|
3
|
+
Generic,
|
|
4
|
+
AsyncGenerator,
|
|
5
|
+
Callable,
|
|
6
|
+
Optional,
|
|
7
|
+
Any,
|
|
8
|
+
Awaitable,
|
|
9
|
+
overload,
|
|
10
|
+
AsyncContextManager,
|
|
11
|
+
)
|
|
12
|
+
from typing_extensions import ParamSpec, Self
|
|
13
|
+
from types import TracebackType
|
|
14
|
+
from abc import ABCMeta
|
|
15
|
+
|
|
16
|
+
from ._typing import AClose, ContextManager, AC, T, R
|
|
17
|
+
|
|
18
|
+
AnyContextManager = AsyncContextManager[T] | ContextManager[T]
|
|
19
|
+
|
|
20
|
+
class ContextDecorator(AsyncContextManager[T], metaclass=ABCMeta):
|
|
21
|
+
"""
|
|
22
|
+
Base class for an async context manager useable as a decorator as well
|
|
23
|
+
|
|
24
|
+
Inheriting from this class adds the scaffolding to automatically enter
|
|
25
|
+
an async context manager on awaiting any callable decorated with it:
|
|
26
|
+
|
|
27
|
+
.. code:: python3
|
|
28
|
+
|
|
29
|
+
class DecoratorAndContext(AsyncContextDecorator):
|
|
30
|
+
async def __aenter__(self) -> Any:
|
|
31
|
+
print("entering", self)
|
|
32
|
+
|
|
33
|
+
async def __aexit__(self, *exc):
|
|
34
|
+
print("exiting", self)
|
|
35
|
+
|
|
36
|
+
@DecoratorAndContext()
|
|
37
|
+
async def func():
|
|
38
|
+
# DecoratorAndContext has been entered already
|
|
39
|
+
print("running some function...")
|
|
40
|
+
# DecoratorAndContext will be exited immediately
|
|
41
|
+
|
|
42
|
+
The context manager can still be used regularly in `async with` statements.
|
|
43
|
+
|
|
44
|
+
Since functions are decorated with an existing context manager instance,
|
|
45
|
+
the same instance is entered and exited on every call. If the context is
|
|
46
|
+
not safe to be entered multiple times or even concurrently the subclass
|
|
47
|
+
should implement the method `_recreate_cm(:Self) -> Self` to create a copy.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
__slots__ = ()
|
|
51
|
+
|
|
52
|
+
def _recreate_cm(self: Self) -> Self: ...
|
|
53
|
+
def __call__(self, func: AC, /) -> AC: ...
|
|
54
|
+
|
|
55
|
+
P = ParamSpec("P")
|
|
56
|
+
|
|
57
|
+
def contextmanager(
|
|
58
|
+
func: Callable[P, AsyncGenerator[T, None]]
|
|
59
|
+
) -> Callable[P, ContextDecorator[T]]: ...
|
|
60
|
+
|
|
61
|
+
class closing(Generic[AClose]):
|
|
62
|
+
def __init__(self, thing: AClose) -> None: ...
|
|
63
|
+
async def __aenter__(self: Self) -> Self: ...
|
|
64
|
+
async def __aexit__(
|
|
65
|
+
self,
|
|
66
|
+
exc_type: type[BaseException] | None,
|
|
67
|
+
exc_val: BaseException | None,
|
|
68
|
+
exc_tb: TracebackType | None,
|
|
69
|
+
) -> bool: ...
|
|
70
|
+
|
|
71
|
+
class nullcontext(AsyncContextManager[T]):
|
|
72
|
+
enter_result: T
|
|
73
|
+
|
|
74
|
+
@overload
|
|
75
|
+
def __init__(self: nullcontext[None], enter_result: None = ...) -> None: ...
|
|
76
|
+
@overload
|
|
77
|
+
def __init__(self: nullcontext[T], enter_result: T) -> None: ...
|
|
78
|
+
async def __aenter__(self: nullcontext[T]) -> T: ...
|
|
79
|
+
async def __aexit__(
|
|
80
|
+
self,
|
|
81
|
+
exc_type: type[BaseException] | None,
|
|
82
|
+
exc_val: BaseException | None,
|
|
83
|
+
exc_tb: TracebackType | None,
|
|
84
|
+
) -> bool: ...
|
|
85
|
+
|
|
86
|
+
SE = TypeVar(
|
|
87
|
+
"SE",
|
|
88
|
+
bound=AsyncContextManager[Any]
|
|
89
|
+
| ContextManager[Any]
|
|
90
|
+
| Callable[
|
|
91
|
+
[type[BaseException] | None, BaseException | None, TracebackType | None],
|
|
92
|
+
Optional[bool],
|
|
93
|
+
]
|
|
94
|
+
| Callable[
|
|
95
|
+
[type[BaseException] | None, BaseException | None, TracebackType | None],
|
|
96
|
+
Awaitable[Optional[bool]],
|
|
97
|
+
],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
class ExitStack:
|
|
101
|
+
def __init__(self) -> None: ...
|
|
102
|
+
def pop_all(self: Self) -> Self: ...
|
|
103
|
+
def push(self, exit: SE) -> SE: ...
|
|
104
|
+
def callback(
|
|
105
|
+
self, callback: Callable[P, R], *args: P.args, **kwargs: P.kwargs
|
|
106
|
+
) -> Callable[P, R]: ...
|
|
107
|
+
async def enter_context(self, cm: AnyContextManager[T]) -> T: ...
|
|
108
|
+
async def aclose(self) -> None: ...
|
|
109
|
+
async def __aenter__(self: Self) -> Self: ...
|
|
110
|
+
async def __aexit__(
|
|
111
|
+
self,
|
|
112
|
+
exc_type: type[BaseException] | None,
|
|
113
|
+
exc_val: BaseException | None,
|
|
114
|
+
tb: TracebackType | None,
|
|
115
|
+
) -> bool: ...
|