ez-a-sync 0.22.6__tar.gz → 0.22.8__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.
Potentially problematic release.
This version of ez-a-sync might be problematic. Click here for more details.
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/.sourcery.yaml +5 -1
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/PKG-INFO +1 -1
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/_typing.py +4 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/_descriptor.py +9 -3
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/method.py +4 -1
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/property.py +34 -2
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/iter.py +90 -32
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/utils/iterators.py +9 -12
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/conf.py +2 -1
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/ez_a_sync.egg-info/PKG-INFO +1 -1
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/.coverage +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/.github/workflows/codeql.yaml +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/.github/workflows/docs.yaml +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/.github/workflows/mypy.yaml +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/.github/workflows/pytest.yaml +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/.github/workflows/release.yaml +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/.gitignore +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/Makefile +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/README.md +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/TODO +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/ENVIRONMENT_VARIABLES.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/_smart.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/_flags.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/_helpers.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/_kwargs.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/_meta.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/abstract.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/base.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/config.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/decorator.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/function.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/modifiers/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/modifiers/cache/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/modifiers/cache/memory.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/modifiers/limiter.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/modifiers/manager.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/modifiers/semaphores.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/a_sync/singleton.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/aliases.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/asyncio/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/asyncio/as_completed.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/asyncio/create_task.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/asyncio/gather.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/asyncio/utils.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/exceptions.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/executor.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/future.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/_debug.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/_loggable.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/locks/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/locks/counter.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/locks/event.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/locks/prio_semaphore.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/locks/semaphore.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/primitives/queue.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/py.typed +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/sphinx/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/sphinx/ext.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/task.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/a_sync/utils/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/Makefile +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/alabaster.css +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/basic.css +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/custom.css +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/doctools.js +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/documentation_options.js +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/file.png +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/language_data.js +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/minus.png +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/plus.png +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/pygments.css +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/searchtools.js +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/_build/html/_static/sphinx_highlight.js +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/index.rst +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/docs/make.bat +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/ez_a_sync.egg-info/SOURCES.txt +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/ez_a_sync.egg-info/dependency_links.txt +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/ez_a_sync.egg-info/requires.txt +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/ez_a_sync.egg-info/top_level.txt +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/requirements-dev.txt +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/requirements.txt +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/setup.cfg +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/setup.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/__init__.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/conftest.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/executor.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/fixtures.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_abstract.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_as_completed.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_base.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_cache.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_decorator.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_executor.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_future.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_gather.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_helpers.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_iter.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_limiter.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_meta.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_modified.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_semaphore.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_singleton.py +0 -0
- {ez_a_sync-0.22.6 → ez_a_sync-0.22.8}/tests/test_task.py +0 -0
|
@@ -31,7 +31,11 @@ ignore: # A list of paths or files which Sourcery will ignore.
|
|
|
31
31
|
rule_settings:
|
|
32
32
|
enable:
|
|
33
33
|
- default
|
|
34
|
-
disable: [
|
|
34
|
+
disable: [
|
|
35
|
+
no-conditionals-in-tests,
|
|
36
|
+
no-loop-in-tests,
|
|
37
|
+
dict-assign-update-to-union, # we want to be backward compatible with many pythons
|
|
38
|
+
] # A list of rule IDs Sourcery will never suggest.
|
|
35
39
|
rule_types:
|
|
36
40
|
- refactoring
|
|
37
41
|
- suggestion
|
|
@@ -25,9 +25,13 @@ T = TypeVar("T")
|
|
|
25
25
|
K = TypeVar("K")
|
|
26
26
|
V = TypeVar("V")
|
|
27
27
|
I = TypeVar("I")
|
|
28
|
+
"""A :class:`TypeVar` that is used to represent instances of a common class."""
|
|
29
|
+
|
|
28
30
|
E = TypeVar('E', bound=Exception)
|
|
29
31
|
TYPE = TypeVar("TYPE", bound=Type)
|
|
32
|
+
|
|
30
33
|
P = ParamSpec("P")
|
|
34
|
+
"""A :class:`ParamSpec` used everywhere in the lib."""
|
|
31
35
|
|
|
32
36
|
Numeric = Union[int, float, Decimal]
|
|
33
37
|
"""Type alias for numeric values of types int, float, or Decimal."""
|
|
@@ -34,7 +34,7 @@ class ASyncDescriptor(ModifiedMixin, Generic[I, P, T]):
|
|
|
34
34
|
**modifiers: ModifierKwargs,
|
|
35
35
|
) -> None:
|
|
36
36
|
"""
|
|
37
|
-
Initialize the
|
|
37
|
+
Initialize the {cls}.
|
|
38
38
|
|
|
39
39
|
Args:
|
|
40
40
|
_fget: The function to be wrapped.
|
|
@@ -56,7 +56,7 @@ class ASyncDescriptor(ModifiedMixin, Generic[I, P, T]):
|
|
|
56
56
|
self.__wrapped__ = _fget
|
|
57
57
|
|
|
58
58
|
self.field_name = field_name or _fget.__name__
|
|
59
|
-
"""The name of the field
|
|
59
|
+
"""The name of the field the {cls} is bound to."""
|
|
60
60
|
|
|
61
61
|
functools.update_wrapper(self, self.__wrapped__)
|
|
62
62
|
|
|
@@ -65,7 +65,7 @@ class ASyncDescriptor(ModifiedMixin, Generic[I, P, T]):
|
|
|
65
65
|
|
|
66
66
|
def __set_name__(self, owner, name):
|
|
67
67
|
"""
|
|
68
|
-
Set the field name when the
|
|
68
|
+
Set the field name when the {cls} is assigned to a class.
|
|
69
69
|
|
|
70
70
|
Args:
|
|
71
71
|
owner: The class owning this descriptor.
|
|
@@ -211,3 +211,9 @@ class ASyncDescriptor(ModifiedMixin, Generic[I, P, T]):
|
|
|
211
211
|
The sum of the results.
|
|
212
212
|
"""
|
|
213
213
|
return await self.map(*instances, concurrency=concurrency, name=name, **kwargs).sum(pop=True, sync=False)
|
|
214
|
+
|
|
215
|
+
def __init_subclass__(cls) -> None:
|
|
216
|
+
for attr in cls.__dict__.values():
|
|
217
|
+
if attr.__doc__ and "{cls}" in attr.__doc__:
|
|
218
|
+
attr.__doc__ = attr.__doc__.replace("{cls}", f":class:`{cls.__name__}`")
|
|
219
|
+
return super().__init_subclass__()
|
|
@@ -31,7 +31,7 @@ class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
|
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
__wrapped__: AnyFn[P, T]
|
|
34
|
-
"""The
|
|
34
|
+
"""The unbound function which will be bound to an instance when :meth:`__get__` is called."""
|
|
35
35
|
|
|
36
36
|
async def __call__(self, instance: I, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
37
37
|
"""
|
|
@@ -267,6 +267,9 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
|
|
|
267
267
|
__weakself__: "weakref.ref[I]"
|
|
268
268
|
"A weak reference to the instance the function is bound to."
|
|
269
269
|
|
|
270
|
+
__wrapped__: AnyFn[Concatenate[I, P], T]
|
|
271
|
+
"""The original unbound method that was wrapped."""
|
|
272
|
+
|
|
270
273
|
__slots__ = "_is_async_def", "__weakself__"
|
|
271
274
|
|
|
272
275
|
def __init__(
|
|
@@ -3,6 +3,7 @@ import functools
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
5
|
import async_property as ap # type: ignore [import]
|
|
6
|
+
from typing_extensions import Unpack
|
|
6
7
|
|
|
7
8
|
from a_sync import _smart, exceptions
|
|
8
9
|
from a_sync._typing import *
|
|
@@ -29,7 +30,7 @@ class _ASyncPropertyDescriptorBase(ASyncDescriptor[I, Tuple[()], T]):
|
|
|
29
30
|
self,
|
|
30
31
|
_fget: AsyncGetterFunction[I, T],
|
|
31
32
|
field_name: Optional[str] = None,
|
|
32
|
-
**modifiers:
|
|
33
|
+
**modifiers: Unpack[ModifierKwargs],
|
|
33
34
|
) -> None:
|
|
34
35
|
super().__init__(_fget, field_name, **modifiers)
|
|
35
36
|
self.hidden_method_name = f"__{self.field_name}__"
|
|
@@ -390,7 +391,14 @@ def a_sync_cached_property( # type: ignore [misc]
|
|
|
390
391
|
|
|
391
392
|
@final
|
|
392
393
|
class HiddenMethod(ASyncBoundMethodAsyncDefault[I, Tuple[()], T]):
|
|
393
|
-
def __init__(
|
|
394
|
+
def __init__(
|
|
395
|
+
self,
|
|
396
|
+
instance: I,
|
|
397
|
+
unbound: AnyFn[Concatenate[I, P], T],
|
|
398
|
+
async_def: bool,
|
|
399
|
+
field_name: str,
|
|
400
|
+
**modifiers: Unpack[ModifierKwargs],
|
|
401
|
+
) -> None:
|
|
394
402
|
super().__init__(instance, unbound, async_def, **modifiers)
|
|
395
403
|
self.__name__ = field_name
|
|
396
404
|
def __repr__(self) -> str:
|
|
@@ -406,6 +414,30 @@ class HiddenMethod(ASyncBoundMethodAsyncDefault[I, Tuple[()], T]):
|
|
|
406
414
|
|
|
407
415
|
@final
|
|
408
416
|
class HiddenMethodDescriptor(ASyncMethodDescriptorAsyncDefault[I, Tuple[()], T]):
|
|
417
|
+
def __init__(
|
|
418
|
+
self,
|
|
419
|
+
_fget: AnyFn[Concatenate[I, P], Awaitable[T]],
|
|
420
|
+
field_name: Optional[str] = None,
|
|
421
|
+
**modifiers: Unpack[ModifierKwargs],
|
|
422
|
+
) -> None:
|
|
423
|
+
"""
|
|
424
|
+
Initialize the HiddenMethodDescriptor.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
_fget: The function to be wrapped.
|
|
428
|
+
field_name: Optional name for the field. If not provided, the function's name will be used.
|
|
429
|
+
**modifiers: Additional modifier arguments.
|
|
430
|
+
|
|
431
|
+
Raises:
|
|
432
|
+
ValueError: If _fget is not callable.
|
|
433
|
+
"""
|
|
434
|
+
super().__init__(_fget, field_name, **modifiers)
|
|
435
|
+
if self.__doc__ is None:
|
|
436
|
+
self.__doc__ = f"A :class:`HiddenMethodDescriptor` for :meth:`{self.__wrapped__.__qualname__}`."
|
|
437
|
+
elif not self.__doc__:
|
|
438
|
+
self.__doc__ += f"A :class:`HiddenMethodDescriptor` for :meth:`{self.__wrapped__.__qualname__}`."
|
|
439
|
+
if self.__wrapped__.__doc__:
|
|
440
|
+
self.__doc__ += f"\n\nThe original docstring for :meth:`~{self.__wrapped__.__qualname__}` is shown below:\n\n{self.__wrapped__.__doc__}"
|
|
409
441
|
def __get__(self, instance: I, owner: Type[I]) -> HiddenMethod[I, T]:
|
|
410
442
|
if instance is None:
|
|
411
443
|
return self
|
|
@@ -43,27 +43,29 @@ class _AwaitableAsyncIterableMixin(AsyncIterable[T]):
|
|
|
43
43
|
```
|
|
44
44
|
"""
|
|
45
45
|
__wrapped__: AsyncIterable[T]
|
|
46
|
-
|
|
47
|
-
def __init_subclass__(cls, **kwargs) -> None:
|
|
48
|
-
new = "When awaited, a list of all elements will be returned."
|
|
49
|
-
if cls.__doc__ is None:
|
|
50
|
-
cls.__doc__ = new
|
|
51
|
-
else:
|
|
52
|
-
cls.__doc__ += f"\n\n{new}"
|
|
53
|
-
return super().__init_subclass__(**kwargs)
|
|
54
46
|
|
|
55
47
|
def __await__(self) -> Generator[Any, Any, List[T]]:
|
|
56
|
-
"""
|
|
48
|
+
"""
|
|
49
|
+
Asynchronously iterate through the {cls} and return all objects.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
A list of the objects yielded by the {cls}.
|
|
53
|
+
"""
|
|
57
54
|
return self._materialized.__await__()
|
|
58
55
|
|
|
59
56
|
@property
|
|
60
57
|
def materialized(self) -> List[T]:
|
|
61
|
-
"""
|
|
58
|
+
"""
|
|
59
|
+
Synchronously iterate through the {cls} and return all objects.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
A list of the objects yielded by the {cls}.
|
|
63
|
+
"""
|
|
62
64
|
return _helpers._await(self._materialized)
|
|
63
65
|
|
|
64
66
|
def sort(self, *, key: SortKey[T] = None, reverse: bool = False) -> "ASyncSorter[T]":
|
|
65
67
|
"""
|
|
66
|
-
|
|
68
|
+
Sort the contents of the {cls}.
|
|
67
69
|
|
|
68
70
|
Args:
|
|
69
71
|
key (optional): A function of one argument that is used to extract a comparison key from each list element. If None, the elements themselves will be sorted. Defaults to None.
|
|
@@ -76,21 +78,43 @@ class _AwaitableAsyncIterableMixin(AsyncIterable[T]):
|
|
|
76
78
|
|
|
77
79
|
def filter(self, function: ViewFn[T]) -> "ASyncFilter[T]":
|
|
78
80
|
"""
|
|
79
|
-
Filters the contents of the
|
|
81
|
+
Filters the contents of the {cls} based on a function.
|
|
80
82
|
|
|
81
83
|
Args:
|
|
82
84
|
function: A function that returns a boolean that indicates if an item should be included in the filtered result. Can be sync or async.
|
|
83
85
|
|
|
84
86
|
Returns:
|
|
85
|
-
An instance of :class:`~ASyncFilter` that
|
|
87
|
+
An instance of :class:`~ASyncFilter` that yields the filtered objects from the {cls}.
|
|
86
88
|
"""
|
|
87
89
|
return ASyncFilter(function, self)
|
|
88
90
|
|
|
89
91
|
@async_cached_property
|
|
90
92
|
async def _materialized(self) -> List[T]:
|
|
91
|
-
"""
|
|
93
|
+
"""
|
|
94
|
+
Asynchronously iterate through the {cls} and return all objects.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
A list of the objects yielded by the {cls}.
|
|
98
|
+
"""
|
|
92
99
|
return [obj async for obj in self]
|
|
93
100
|
|
|
101
|
+
def __init_subclass__(cls, **kwargs) -> None:
|
|
102
|
+
new = "When awaited, a list of all elements will be returned."
|
|
103
|
+
|
|
104
|
+
# modify the class docstring
|
|
105
|
+
if cls.__doc__ is None:
|
|
106
|
+
cls.__doc__ = new
|
|
107
|
+
else:
|
|
108
|
+
cls.__doc__ += f"\n\n{new}"
|
|
109
|
+
|
|
110
|
+
# format the member docstrings
|
|
111
|
+
for attr_name in dir(cls):
|
|
112
|
+
attr = getattr(cls, attr_name)
|
|
113
|
+
if attr.__doc__ and "{cls}" in attr.__doc__:
|
|
114
|
+
attr.__doc__ = attr.__doc__.replace("{cls}", f":class:`{cls.__name__}`")
|
|
115
|
+
|
|
116
|
+
return super().__init_subclass__(**kwargs)
|
|
117
|
+
|
|
94
118
|
__slots__ = '__async_property__',
|
|
95
119
|
|
|
96
120
|
class ASyncIterable(_AwaitableAsyncIterableMixin[T], Iterable[T]):
|
|
@@ -116,11 +140,13 @@ class ASyncIterable(_AwaitableAsyncIterableMixin[T], Iterable[T]):
|
|
|
116
140
|
return f"<{type(self).__name__} for {self.__wrapped__} at {hex(id(self))}>"
|
|
117
141
|
|
|
118
142
|
def __aiter__(self) -> AsyncIterator[T]:
|
|
119
|
-
"
|
|
143
|
+
"""
|
|
144
|
+
Return an async iterator that yields :obj:`T` objects from the {cls}.
|
|
145
|
+
"""
|
|
120
146
|
return self.__wrapped__.__aiter__()
|
|
121
147
|
|
|
122
148
|
def __iter__(self) -> Iterator[T]:
|
|
123
|
-
"
|
|
149
|
+
"Return an iterator that yields :obj:`T` objects from the {cls}."
|
|
124
150
|
yield from ASyncIterator(self.__aiter__())
|
|
125
151
|
__slots__ = "__wrapped__",
|
|
126
152
|
|
|
@@ -128,14 +154,20 @@ AsyncGenFunc = Callable[P, Union[AsyncGenerator[T, None], AsyncIterator[T]]]
|
|
|
128
154
|
|
|
129
155
|
class ASyncIterator(_AwaitableAsyncIterableMixin[T], Iterator[T]):
|
|
130
156
|
"""
|
|
131
|
-
|
|
132
|
-
A hybrid Iterator/AsyncIterator implementation that bridges the gap between synchronous and asynchronous iteration. This class provides a unified interface for iteration that can seamlessly operate in both synchronous (`for` loop) and asynchronous (`async for` loop) contexts. It allows the wrapping of asynchronous iterable objects or async generator functions, making them usable in synchronous code without explicitly managing event loops or asynchronous context switches.
|
|
157
|
+
A hybrid Iterator/AsyncIterator implementation that bridges the gap between synchronous and asynchronous iteration. This class provides a unified interface for iteration that can seamlessly operate in both synchronous (`for` loop) and asynchronous (`async for` loop) contexts. It allows the wrapping of asynchronous iterable objects or async generator functions, making them usable in synchronous code without explicitly managing event loops or asynchronous context switches.
|
|
133
158
|
|
|
134
|
-
|
|
159
|
+
By implementing both `__next__` and `__anext__` methods, ASyncIterator enables objects to be iterated using standard iteration protocols while internally managing the complexities of asynchronous iteration. This design simplifies the use of asynchronous iterables in environments or frameworks that are not inherently asynchronous, such as standard synchronous functions or older codebases being gradually migrated to asynchronous IO.
|
|
135
160
|
|
|
136
|
-
|
|
161
|
+
This class is particularly useful for library developers seeking to provide a consistent iteration interface across synchronous and asynchronous code, reducing the cognitive load on users and promoting code reusability and simplicity.
|
|
137
162
|
"""
|
|
163
|
+
|
|
138
164
|
def __next__(self) -> T:
|
|
165
|
+
"""
|
|
166
|
+
Synchronously fetch the next item from the {cls}.
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
:class:`StopIteration`: Once all items have been fetched from the {cls}.
|
|
170
|
+
"""
|
|
139
171
|
try:
|
|
140
172
|
return asyncio.get_event_loop().run_until_complete(self.__anext__())
|
|
141
173
|
except StopAsyncIteration as e:
|
|
@@ -164,24 +196,32 @@ class ASyncIterator(_AwaitableAsyncIterableMixin[T], Iterator[T]):
|
|
|
164
196
|
if not isinstance(async_iterator, AsyncIterator):
|
|
165
197
|
raise TypeError(f"`async_iterator` must be an AsyncIterator. You passed {async_iterator}")
|
|
166
198
|
self.__wrapped__ = async_iterator
|
|
167
|
-
"The wrapped
|
|
199
|
+
"The wrapped :class:`AsyncIterator`."
|
|
168
200
|
|
|
169
201
|
async def __anext__(self) -> T:
|
|
170
|
-
"
|
|
202
|
+
"""
|
|
203
|
+
Asynchronously fetch the next item from the {cls}.
|
|
204
|
+
|
|
205
|
+
Raises:
|
|
206
|
+
:class:`StopAsyncIteration`: Once all items have been fetched from the {cls}.
|
|
207
|
+
"""
|
|
171
208
|
return await self.__wrapped__.__anext__()
|
|
172
209
|
|
|
173
|
-
def
|
|
174
|
-
"
|
|
210
|
+
def __iter__(self) -> Self:
|
|
211
|
+
"Return the {cls} for iteration."
|
|
212
|
+
return self
|
|
213
|
+
|
|
214
|
+
def __aiter__(self) -> Self:
|
|
215
|
+
"Return the {cls} for aiteration."
|
|
175
216
|
return self
|
|
176
217
|
|
|
177
218
|
class ASyncGeneratorFunction(Generic[P, T]):
|
|
178
219
|
"""
|
|
179
|
-
|
|
180
|
-
Encapsulates an asynchronous generator function, providing a mechanism to use it as an asynchronous iterator with enhanced capabilities. This class wraps an async generator function, allowing it to be called with parameters and return an :class:`~ASyncIterator` object. It is particularly useful for situations where an async generator function needs to be used in a manner that is consistent with both synchronous and asynchronous execution contexts.
|
|
220
|
+
Encapsulates an asynchronous generator function, providing a mechanism to use it as an asynchronous iterator with enhanced capabilities. This class wraps an async generator function, allowing it to be called with parameters and return an :class:`~ASyncIterator` object. It is particularly useful for situations where an async generator function needs to be used in a manner that is consistent with both synchronous and asynchronous execution contexts.
|
|
181
221
|
|
|
182
|
-
|
|
222
|
+
The ASyncGeneratorFunction class supports dynamic binding to instances, enabling it to be used as a method on class instances. When accessed as a descriptor, it automatically handles the binding to the instance, thereby allowing the wrapped async generator function to be invoked with instance context ('self') automatically provided. This feature is invaluable for designing classes that need to expose asynchronous generators as part of their interface while maintaining the ease of use and calling semantics similar to regular methods.
|
|
183
223
|
|
|
184
|
-
|
|
224
|
+
By providing a unified interface to asynchronous generator functions, this class facilitates the creation of APIs that are flexible and easy to use in a wide range of asynchronous programming scenarios. It abstracts away the complexities involved in managing asynchronous generator lifecycles and invocation semantics, making it easier for developers to integrate asynchronous iteration patterns into their applications.
|
|
185
225
|
"""
|
|
186
226
|
|
|
187
227
|
_cache_handle: asyncio.TimerHandle
|
|
@@ -191,11 +231,20 @@ class ASyncGeneratorFunction(Generic[P, T]):
|
|
|
191
231
|
"A weak reference to the instance the function is bound to, if any."
|
|
192
232
|
|
|
193
233
|
def __init__(self, async_gen_func: AsyncGenFunc[P, T], instance: Any = None) -> None:
|
|
194
|
-
"
|
|
234
|
+
"""
|
|
235
|
+
Initializes the ASyncGeneratorFunction with the given async generator function and optionally an instance.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
async_gen_func: The async generator function to wrap.
|
|
239
|
+
instance (optional): The object to bind to the function, if applicable.
|
|
240
|
+
"""
|
|
241
|
+
|
|
195
242
|
self.field_name = async_gen_func.__name__
|
|
196
243
|
"The name of the async generator function."
|
|
244
|
+
|
|
197
245
|
self.__wrapped__ = async_gen_func
|
|
198
246
|
"The actual async generator function."
|
|
247
|
+
|
|
199
248
|
if instance is not None:
|
|
200
249
|
self._cache_handle = self.__get_cache_handle(instance)
|
|
201
250
|
self.__weakself__ = weakref.ref(instance, self.__cancel_cache_handle)
|
|
@@ -205,7 +254,16 @@ class ASyncGeneratorFunction(Generic[P, T]):
|
|
|
205
254
|
return f"<{type(self).__name__} for {self.__wrapped__} at {hex(id(self))}>"
|
|
206
255
|
|
|
207
256
|
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> ASyncIterator[T]:
|
|
208
|
-
"
|
|
257
|
+
"""
|
|
258
|
+
Calls the wrapped async generator function with the given arguments and keyword arguments, returning an :class:`ASyncIterator`.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
*args: Positional arguments for the function.
|
|
262
|
+
**kwargs: Keyword arguments for the function.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
An :class:`ASyncIterator` wrapping the :class:`AsyncIterator` returned from the wrapped function call.
|
|
266
|
+
"""
|
|
209
267
|
if self.__weakself__ is None:
|
|
210
268
|
return ASyncIterator(self.__wrapped__(*args, **kwargs))
|
|
211
269
|
return ASyncIterator(self.__wrapped__(self.__self__, *args, **kwargs))
|
|
@@ -257,7 +315,7 @@ class _ASyncView(ASyncIterator[T]):
|
|
|
257
315
|
iterable: AnyIterable[T],
|
|
258
316
|
) -> None:
|
|
259
317
|
"""
|
|
260
|
-
Initializes the
|
|
318
|
+
Initializes the {cls} with a function and an iterable.
|
|
261
319
|
|
|
262
320
|
Args:
|
|
263
321
|
function: A function to apply to the items in the iterable.
|
|
@@ -360,7 +418,7 @@ class ASyncSorter(_ASyncView[T]):
|
|
|
360
418
|
|
|
361
419
|
def __aiter__(self) -> AsyncIterator[T]:
|
|
362
420
|
"""
|
|
363
|
-
|
|
421
|
+
Return an async iterator for the {cls}.
|
|
364
422
|
|
|
365
423
|
Raises:
|
|
366
424
|
RuntimeError: If the ASyncSorter instance has already been consumed.
|
|
@@ -13,10 +13,9 @@ logger = logging.getLogger(__name__)
|
|
|
13
13
|
|
|
14
14
|
async def exhaust_iterator(iterator: AsyncIterator[T], *, queue: Optional[asyncio.Queue] = None) -> None:
|
|
15
15
|
"""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
This function is a utility to exhaust an async iterator, with an option to forward the iterated items to a provided asyncio.Queue. It's particularly useful when dealing with asynchronous operations that produce items to be consumed by other parts of an application, enabling a producer-consumer pattern.
|
|
16
|
+
Asynchronously iterates over items from the given async iterator and optionally places them into a queue.
|
|
17
|
+
|
|
18
|
+
This function is a utility to exhaust an async iterator, with an option to forward the iterated items to a provided asyncio.Queue. It's particularly useful when dealing with asynchronous operations that produce items to be consumed by other parts of an application, enabling a producer-consumer pattern.
|
|
20
19
|
|
|
21
20
|
Args:
|
|
22
21
|
iterator (AsyncIterator[T]): The async iterator to exhaust.
|
|
@@ -33,10 +32,9 @@ async def exhaust_iterator(iterator: AsyncIterator[T], *, queue: Optional[asynci
|
|
|
33
32
|
|
|
34
33
|
async def exhaust_iterators(iterators, *, queue: Optional[asyncio.Queue] = None, join: bool = False) -> None:
|
|
35
34
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
This function leverages asyncio.gather to concurrently exhaust multiple async iterators. It's useful in scenarios where items from multiple async sources need to be processed or collected together, supporting concurrent operations and efficient multitasking.
|
|
35
|
+
Asynchronously iterates over multiple async iterators concurrently and optionally places their items into a queue.
|
|
36
|
+
|
|
37
|
+
This function leverages asyncio.gather to concurrently exhaust multiple async iterators. It's useful in scenarios where items from multiple async sources need to be processed or collected together, supporting concurrent operations and efficient multitasking.
|
|
40
38
|
|
|
41
39
|
Args:
|
|
42
40
|
iterators: A sequence of async iterators to be exhausted concurrently.
|
|
@@ -93,12 +91,11 @@ def as_yielded(iterator0: AsyncIterator[T0], iterator1: AsyncIterator[T1]) -> As
|
|
|
93
91
|
def as_yielded(iterator0: AsyncIterator[T0], iterator1: AsyncIterator[T1], iterator2: AsyncIterator[T2], *iterators: AsyncIterator[T]) -> AsyncIterator[Union[T0, T1, T2, T]]:...
|
|
94
92
|
async def as_yielded(*iterators: AsyncIterator[T]) -> AsyncIterator[T]: # type: ignore [misc]
|
|
95
93
|
"""
|
|
96
|
-
|
|
97
|
-
Merges multiple async iterators into a single async iterator that yields items as they become available from any of the source iterators.
|
|
94
|
+
Merges multiple async iterators into a single async iterator that yields items as they become available from any of the source iterators.
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
This function is designed to streamline the handling of multiple asynchronous data streams by consolidating them into a single asynchronous iteration context. It enables concurrent fetching and processing of items from multiple sources, improving efficiency and simplifying code structure when dealing with asynchronous operations.
|
|
100
97
|
|
|
101
|
-
|
|
98
|
+
The merging process is facilitated by internally managing a queue where items from the source iterators are placed as they are fetched. This mechanism ensures that the merged stream of items is delivered in an order determined by the availability of items from the source iterators, rather than their original sequence.
|
|
102
99
|
|
|
103
100
|
Args:
|
|
104
101
|
*iterators: Variable length list of AsyncIterator objects to be merged.
|
|
@@ -65,6 +65,7 @@ autodoc_default_options = {
|
|
|
65
65
|
'_ASyncSingletonMeta__lock',
|
|
66
66
|
'_is_protocol',
|
|
67
67
|
'_is_runtime_protocol',
|
|
68
|
+
'_materialized',
|
|
68
69
|
]),
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -88,7 +89,7 @@ SKIP_MODULES = [
|
|
|
88
89
|
|
|
89
90
|
def skip_undesired_members(app, what, name, obj, skip, options):
|
|
90
91
|
# skip some submodules (not sure if this works right or if its even desired)
|
|
91
|
-
if what == "module" and obj
|
|
92
|
+
if what == "module" and getattr(obj, '__name__', None) in SKIP_MODULES:
|
|
92
93
|
skip = True
|
|
93
94
|
|
|
94
95
|
# Skip the __init__, __str__, __getattribute__, args, and with_traceback members of all Exceptions
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|