asyncstdlib 3.12.0__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.
Files changed (33) hide show
  1. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/PKG-INFO +3 -5
  2. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/__init__.py +10 -2
  3. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/_core.py +22 -16
  4. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/_lrucache.py +47 -28
  5. asyncstdlib-3.12.2/asyncstdlib/_lrucache.pyi +90 -0
  6. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/_typing.py +17 -12
  7. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/_utility.py +1 -19
  8. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/asynctools.py +49 -61
  9. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/builtins.py +8 -313
  10. asyncstdlib-3.12.2/asyncstdlib/builtins.pyi +239 -0
  11. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/contextlib.py +81 -58
  12. asyncstdlib-3.12.2/asyncstdlib/contextlib.pyi +115 -0
  13. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/functools.py +15 -8
  14. asyncstdlib-3.12.2/asyncstdlib/functools.pyi +26 -0
  15. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/heapq.py +15 -43
  16. asyncstdlib-3.12.2/asyncstdlib/heapq.pyi +40 -0
  17. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/itertools.py +25 -157
  18. asyncstdlib-3.12.2/asyncstdlib/itertools.pyi +238 -0
  19. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/pyproject.toml +3 -4
  20. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/test_contextlib.py +22 -3
  21. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/test_functools.py +3 -1
  22. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/test_helpers.py +4 -19
  23. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/utility.py +8 -8
  24. asyncstdlib-3.12.0/asyncstdlib/_lrucache.pyi +0 -61
  25. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/LICENSE +0 -0
  26. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/README.rst +0 -0
  27. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/asyncstdlib/py.typed +0 -0
  28. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/__init__.py +0 -0
  29. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/test_asynctools.py +0 -0
  30. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/test_builtins.py +0 -0
  31. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/test_functools_lru.py +0 -0
  32. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/test_heapq.py +0 -0
  33. {asyncstdlib-3.12.0 → asyncstdlib-3.12.2}/unittests/test_itertools.py +0 -0
@@ -1,24 +1,21 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: asyncstdlib
3
- Version: 3.12.0
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>
7
- Requires-Python: ~=3.6
7
+ Requires-Python: ~=3.8
8
8
  Description-Content-Type: text/x-rst
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Framework :: AsyncIO
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: MIT License
13
13
  Classifier: Programming Language :: Python :: 3 :: Only
14
- Classifier: Programming Language :: Python :: 3.6
15
- Classifier: Programming Language :: Python :: 3.7
16
14
  Classifier: Programming Language :: Python :: 3.8
17
15
  Classifier: Programming Language :: Python :: 3.9
18
16
  Classifier: Programming Language :: Python :: 3.10
19
17
  Classifier: Programming Language :: Python :: 3.11
20
18
  Classifier: Programming Language :: Python :: 3.12
21
- Requires-Dist: typing_extensions; python_version<'3.8'
22
19
  Requires-Dist: sphinx ; extra == "doc"
23
20
  Requires-Dist: sphinxcontrib-trio ; extra == "doc"
24
21
  Requires-Dist: pytest ; extra == "test"
@@ -29,6 +26,7 @@ Requires-Dist: coverage ; extra == "test"
29
26
  Requires-Dist: pytest-cov ; extra == "test"
30
27
  Requires-Dist: flake8-2020 ; extra == "test"
31
28
  Requires-Dist: mypy ; extra == "test" and ( implementation_name=='cpython')
29
+ Requires-Dist: typing-extensions ; extra == "test"
32
30
  Project-URL: Documentation, https://asyncstdlib.readthedocs.io/en/latest/
33
31
  Project-URL: Source, https://github.com/maxfischer2781/asyncstdlib
34
32
  Provides-Extra: doc
@@ -1,4 +1,5 @@
1
1
  """The missing async toolbox"""
2
+
2
3
  from .builtins import (
3
4
  anext,
4
5
  zip,
@@ -18,7 +19,13 @@ from .builtins import (
18
19
  sorted,
19
20
  )
20
21
  from .functools import reduce, lru_cache, cache, cached_property
21
- from .contextlib import closing, contextmanager, nullcontext, ExitStack
22
+ from .contextlib import (
23
+ closing,
24
+ ContextDecorator,
25
+ contextmanager,
26
+ nullcontext,
27
+ ExitStack,
28
+ )
22
29
  from .itertools import (
23
30
  accumulate,
24
31
  batched,
@@ -38,7 +45,7 @@ from .itertools import (
38
45
  from .asynctools import borrow, scoped_iter, await_each, any_iter, apply, sync
39
46
  from .heapq import merge, nlargest, nsmallest
40
47
 
41
- __version__ = "3.12.0"
48
+ __version__ = "3.12.2"
42
49
 
43
50
  __all__ = [
44
51
  "anext",
@@ -64,6 +71,7 @@ __all__ = [
64
71
  "cached_property",
65
72
  # contextlib
66
73
  "closing",
74
+ "ContextDecorator",
67
75
  "contextmanager",
68
76
  "nullcontext",
69
77
  "ExitStack",
@@ -1,3 +1,13 @@
1
+ """
2
+ Internal helpers to safely build async abstractions
3
+
4
+ While some of these helpers have public siblings
5
+ (e.g. :py:class:`~.ScopedIter` and :py:func:`~.asynctools.scoped_iter`)
6
+ they are purposely kept separate.
7
+ Any helpers in this module are *not* bound to maintaining a public interface,
8
+ and offer less convenience to save on overhead.
9
+ """
10
+
1
11
  from inspect import iscoroutinefunction
2
12
  from typing import (
3
13
  Any,
@@ -55,20 +65,19 @@ async def _aiter_sync(iterable: Iterable[T]) -> AsyncIterator[T]:
55
65
 
56
66
 
57
67
  class ScopedIter(Generic[T]):
58
- """Context manager that provides and cleans up an iterator for an iterable"""
68
+ """
69
+ Context manager that provides and cleans up an iterator for an iterable
59
70
 
60
- __slots__ = ("_iterable", "_iterator")
71
+ Note that unlike :py:func:`~.asynctools.scoped_iter`, this helper does
72
+ *not* borrow the iterator automatically. Use :py:func:`~.borrow` if needed.
73
+ """
74
+
75
+ __slots__ = ("_iterator",)
61
76
 
62
77
  def __init__(self, iterable: AnyIterable[T]):
63
- self._iterable: Optional[AnyIterable[T]] = iterable
64
- self._iterator: Optional[AsyncIterator[T]] = None
78
+ self._iterator: AsyncIterator[T] = aiter(iterable)
65
79
 
66
80
  async def __aenter__(self) -> AsyncIterator[T]:
67
- assert (
68
- self._iterable is not None
69
- ), f"{self.__class__.__name__} is not re-entrant"
70
- self._iterator = aiter(self._iterable)
71
- self._iterable = None
72
81
  return self._iterator
73
82
 
74
83
  async def __aexit__(
@@ -76,20 +85,18 @@ class ScopedIter(Generic[T]):
76
85
  exc_type: Optional[Type[BaseException]],
77
86
  exc_val: Optional[BaseException],
78
87
  exc_tb: Optional[TracebackType],
79
- ) -> bool:
88
+ ) -> None:
80
89
  try:
81
90
  aclose = self._iterator.aclose() # type: ignore
82
91
  except AttributeError:
83
92
  pass
84
93
  else:
85
94
  await aclose
86
- return False
87
95
 
88
96
 
89
- async def borrow(iterator: AsyncIterator[T]) -> AsyncGenerator[T, None]:
97
+ def borrow(iterator: AsyncIterator[T]) -> AsyncGenerator[T, None]:
90
98
  """Borrow an async iterator for iteration, preventing it from being closed"""
91
- async for item in iterator:
92
- yield item
99
+ return (item async for item in iterator)
93
100
 
94
101
 
95
102
  def awaitify(
@@ -112,8 +119,7 @@ class Awaitify(Generic[T]):
112
119
  self._async_call: Optional[Callable[..., Awaitable[T]]] = None
113
120
 
114
121
  def __call__(self, *args: Any, **kwargs: Any) -> Awaitable[T]:
115
- async_call = self._async_call
116
- if async_call is None:
122
+ if (async_call := self._async_call) is None:
117
123
  value = self.__wrapped__(*args, **kwargs)
118
124
  if isinstance(value, Awaitable):
119
125
  self._async_call = self.__wrapped__ # type: ignore
@@ -5,7 +5,11 @@ This is loosely based on the CPython 3.8 implementation. In specific,
5
5
  several performance hacks are skipped in favour of maintainability,
6
6
  especially when they might not apply to PyPy.
7
7
  """
8
+
9
+ from __future__ import annotations
8
10
  from typing import (
11
+ Generic,
12
+ TypeVar,
9
13
  NamedTuple,
10
14
  Callable,
11
15
  Any,
@@ -71,20 +75,31 @@ class LRUAsyncCallable(Protocol[AC]):
71
75
  """The callable wrapped by this cache"""
72
76
  raise NotImplementedError
73
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
+
74
86
  #: Get the result of ``await __wrapped__(...)`` from the cache or evaluation
75
87
  __call__: AC
76
88
 
77
89
  def cache_parameters(self) -> CacheParameters:
78
90
  """Get the parameters of the cache"""
91
+ ...
79
92
 
80
93
  def cache_info(self) -> CacheInfo:
81
94
  """
82
95
  Get the current performance and boundary of the cache
83
96
  as a :py:class:`~typing.NamedTuple`
84
97
  """
98
+ ...
85
99
 
86
100
  def cache_clear(self) -> None:
87
101
  """Evict all call argument patterns and their results from the cache"""
102
+ ...
88
103
 
89
104
  def cache_discard(self, *args: Any, **kwargs: Any) -> None:
90
105
  """
@@ -95,28 +110,44 @@ class LRUAsyncCallable(Protocol[AC]):
95
110
  the descriptor must support wrapping descriptors for this method
96
111
  to detect implicit arguments such as ``self``.
97
112
  """
113
+ # Maintainers note:
98
114
  # "support wrapping descriptors" means that the wrapping descriptor has to use
99
115
  # the cache as a descriptor as well, i.e. invoke its ``__get__`` method instead
100
116
  # of just passing in `self`/`cls`/... directly.
117
+ ...
118
+
119
+
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")
101
125
 
102
126
 
103
- class LRUAsyncBoundCallable(LRUAsyncCallable[AC]):
127
+ class LRUAsyncBoundCallable(Generic[S, P, R]): # type: ignore[reportInvalidTypeVarUse]
104
128
  """A :py:class:`~.LRUAsyncCallable` that is bound like a method"""
105
129
 
106
130
  __slots__ = ("__weakref__", "_lru", "__self__")
107
131
 
108
- def __init__(self, lru: LRUAsyncCallable[AC], __self__: object):
132
+ def __init__(self, lru: LRUAsyncCallable[Any], __self__: object):
109
133
  self._lru = lru
110
134
  self.__self__ = __self__
111
135
 
112
136
  @property
113
- def __wrapped__(self) -> AC:
137
+ def __wrapped__(self) -> Any:
114
138
  return self._lru.__wrapped__
115
139
 
116
140
  @property
117
- def __func__(self) -> LRUAsyncCallable[AC]:
141
+ def __func__(self) -> LRUAsyncCallable[Any]:
118
142
  return self._lru
119
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
+
120
151
  def __call__(self, *args, **kwargs): # type: ignore
121
152
  return self._lru(self.__self__, *args, **kwargs)
122
153
 
@@ -149,15 +180,13 @@ class LRUAsyncBoundCallable(LRUAsyncCallable[AC]):
149
180
 
150
181
 
151
182
  @overload
152
- def lru_cache(maxsize: AC, typed: bool = ...) -> LRUAsyncCallable[AC]:
153
- ...
183
+ def lru_cache(maxsize: AC, typed: bool = ...) -> LRUAsyncCallable[AC]: ...
154
184
 
155
185
 
156
186
  @overload
157
187
  def lru_cache(
158
188
  maxsize: Optional[int] = ..., typed: bool = ...
159
- ) -> Callable[[AC], LRUAsyncCallable[AC]]:
160
- ...
189
+ ) -> Callable[[AC], LRUAsyncCallable[AC]]: ...
161
190
 
162
191
 
163
192
  @public_module("asyncstdlib.functools")
@@ -212,7 +241,8 @@ def lru_cache(
212
241
  elif callable(maxsize):
213
242
  # used as function decorator, first arg is the function to be wrapped
214
243
  fast_wrapper = CachedLRUAsyncCallable(cast(AC, maxsize), typed, 128)
215
- return update_wrapper(fast_wrapper, maxsize)
244
+ update_wrapper(fast_wrapper, maxsize)
245
+ return fast_wrapper
216
246
  elif maxsize is not None:
217
247
  raise TypeError(
218
248
  "first argument to 'lru_cache' must be an int, a callable or None"
@@ -227,7 +257,8 @@ def lru_cache(
227
257
  wrapper = UncachedLRUAsyncCallable(function, typed)
228
258
  else:
229
259
  wrapper = CachedLRUAsyncCallable(function, typed, maxsize)
230
- return update_wrapper(wrapper, function)
260
+ update_wrapper(wrapper, function)
261
+ return wrapper
231
262
 
232
263
  return lru_decorator
233
264
 
@@ -283,29 +314,19 @@ class CallKey:
283
314
  return cls(key)
284
315
 
285
316
 
286
- def cache__get(
287
- self: LRUAsyncCallable[AC], instance: object, owner: Optional[type] = None
288
- ) -> LRUAsyncCallable[AC]:
289
- """Descriptor ``__get__`` for caches to bind them on lookup"""
290
- if instance is None:
291
- return self
292
- return LRUAsyncBoundCallable(self, instance)
293
-
294
-
295
317
  class UncachedLRUAsyncCallable(LRUAsyncCallable[AC]):
296
318
  """Wrap the async ``call`` to track accesses as for caching/memoization"""
297
319
 
298
320
  __slots__ = ("__weakref__", "__dict__", "__wrapped__", "__misses", "__typed")
299
321
 
300
322
  __wrapped__: AC
301
- __get__ = cache__get
302
323
 
303
324
  def __init__(self, call: AC, typed: bool):
304
- self.__wrapped__ = call
325
+ self.__wrapped__ = call # type: ignore[reportIncompatibleMethodOverride]
305
326
  self.__misses = 0
306
327
  self.__typed = typed
307
328
 
308
- async def __call__(self, *args, **kwargs): # type: ignore
329
+ async def __call__(self, *args: Any, **kwargs: Any) -> Any: # type: ignore[reportIncompatibleVariableOverride]
309
330
  self.__misses += 1
310
331
  return await self.__wrapped__(*args, **kwargs)
311
332
 
@@ -336,16 +357,15 @@ class MemoizedLRUAsyncCallable(LRUAsyncCallable[AC]):
336
357
  )
337
358
 
338
359
  __wrapped__: AC
339
- __get__ = cache__get
340
360
 
341
361
  def __init__(self, call: AC, typed: bool):
342
- self.__wrapped__ = call
362
+ self.__wrapped__ = call # type: ignore[reportIncompatibleMethodOverride]
343
363
  self.__hits = 0
344
364
  self.__misses = 0
345
365
  self.__typed = typed
346
366
  self.__cache: Dict[Union[CallKey, int, str], Any] = {}
347
367
 
348
- async def __call__(self, *args, **kwargs): # type: ignore
368
+ async def __call__(self, *args: Any, **kwargs: Any) -> Any: # type: ignore[reportIncompatibleVariableOverride]
349
369
  key = CallKey.from_call(args, kwargs, typed=self.__typed)
350
370
  try:
351
371
  result = self.__cache[key]
@@ -391,17 +411,16 @@ class CachedLRUAsyncCallable(LRUAsyncCallable[AC]):
391
411
  )
392
412
 
393
413
  __wrapped__: AC
394
- __get__ = cache__get
395
414
 
396
415
  def __init__(self, call: AC, typed: bool, maxsize: int):
397
- self.__wrapped__ = call
416
+ self.__wrapped__ = call # type: ignore[reportIncompatibleMethodOverride]
398
417
  self.__hits = 0
399
418
  self.__misses = 0
400
419
  self.__typed = typed
401
420
  self.__maxsize = maxsize
402
421
  self.__cache: OrderedDict[Union[int, str, CallKey], Any] = OrderedDict()
403
422
 
404
- async def __call__(self, *args, **kwargs): # type: ignore
423
+ async def __call__(self, *args: Any, **kwargs: Any) -> Any: # type: ignore[reportIncompatibleVariableOverride]
405
424
  key = CallKey.from_call(args, kwargs, typed=self.__typed)
406
425
  try:
407
426
  result = self.__cache[key]
@@ -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]]: ...
@@ -4,7 +4,7 @@ Helper module to simplify version specific typing imports
4
4
  This module is for internal use only. Do *not* put any new
5
5
  "async typing" definitions here.
6
6
  """
7
- import sys
7
+
8
8
  from typing import (
9
9
  TypeVar,
10
10
  Hashable,
@@ -14,21 +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
- if sys.version_info >= (3, 8):
20
- from typing import Protocol, AsyncContextManager, ContextManager, TypedDict
21
- else:
22
- from typing_extensions import (
23
- Protocol,
24
- AsyncContextManager,
25
- ContextManager,
26
- TypedDict,
27
- )
28
-
29
23
  __all__ = [
30
24
  "Protocol",
31
- "AsyncContextManager",
32
25
  "ContextManager",
33
26
  "TypedDict",
34
27
  "T",
@@ -43,6 +36,8 @@ __all__ = [
43
36
  "HK",
44
37
  "LT",
45
38
  "ADD",
39
+ "AClose",
40
+ "ACloseable",
46
41
  "AnyIterable",
47
42
  ]
48
43
 
@@ -78,5 +73,15 @@ class SupportsAdd(Protocol):
78
73
  raise NotImplementedError
79
74
 
80
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
+
81
86
  #: (async) iter T
82
87
  AnyIterable = Union[Iterable[T], AsyncIterable[T]]
@@ -1,4 +1,4 @@
1
- from typing import TypeVar, Any, Optional, Callable
1
+ from typing import TypeVar, Optional, Callable
2
2
 
3
3
  from ._typing import Protocol
4
4
 
@@ -29,21 +29,3 @@ def public_module(
29
29
  return thing
30
30
 
31
31
  return decorator
32
-
33
-
34
- def slot_get(instance: object, name: str) -> Any:
35
- """
36
- Emulate ``instance.name`` using slot lookup as used for special methods
37
-
38
- This invokes the descriptor protocol, i.e. it calls the attribute's
39
- ``__get__`` if available.
40
- """
41
-
42
- owner = type(instance)
43
- attribute = getattr(owner, name)
44
- try:
45
- descriptor_get = attribute.__get__
46
- except AttributeError:
47
- return attribute
48
- else:
49
- return descriptor_get(instance, owner)