ez-a-sync 0.22.14__py3-none-any.whl → 0.22.16__py3-none-any.whl
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.
- a_sync/ENVIRONMENT_VARIABLES.py +37 -5
- a_sync/__init__.py +53 -12
- a_sync/_smart.py +231 -28
- a_sync/_typing.py +112 -15
- a_sync/a_sync/__init__.py +35 -10
- a_sync/a_sync/_descriptor.py +248 -38
- a_sync/a_sync/_flags.py +78 -9
- a_sync/a_sync/_helpers.py +46 -13
- a_sync/a_sync/_kwargs.py +33 -8
- a_sync/a_sync/_meta.py +149 -28
- a_sync/a_sync/abstract.py +150 -28
- a_sync/a_sync/base.py +34 -16
- a_sync/a_sync/config.py +85 -14
- a_sync/a_sync/decorator.py +441 -139
- a_sync/a_sync/function.py +709 -147
- a_sync/a_sync/method.py +437 -110
- a_sync/a_sync/modifiers/__init__.py +85 -5
- a_sync/a_sync/modifiers/cache/__init__.py +116 -17
- a_sync/a_sync/modifiers/cache/memory.py +130 -20
- a_sync/a_sync/modifiers/limiter.py +101 -22
- a_sync/a_sync/modifiers/manager.py +142 -16
- a_sync/a_sync/modifiers/semaphores.py +121 -15
- a_sync/a_sync/property.py +383 -82
- a_sync/a_sync/singleton.py +44 -19
- a_sync/aliases.py +0 -1
- a_sync/asyncio/__init__.py +140 -1
- a_sync/asyncio/as_completed.py +213 -79
- a_sync/asyncio/create_task.py +70 -20
- a_sync/asyncio/gather.py +125 -58
- a_sync/asyncio/utils.py +3 -3
- a_sync/exceptions.py +248 -26
- a_sync/executor.py +164 -69
- a_sync/future.py +1227 -168
- a_sync/iter.py +173 -56
- a_sync/primitives/__init__.py +14 -2
- a_sync/primitives/_debug.py +72 -18
- a_sync/primitives/_loggable.py +41 -10
- a_sync/primitives/locks/__init__.py +5 -2
- a_sync/primitives/locks/counter.py +107 -38
- a_sync/primitives/locks/event.py +21 -7
- a_sync/primitives/locks/prio_semaphore.py +262 -63
- a_sync/primitives/locks/semaphore.py +138 -89
- a_sync/primitives/queue.py +601 -60
- a_sync/sphinx/__init__.py +0 -1
- a_sync/sphinx/ext.py +160 -50
- a_sync/task.py +313 -112
- a_sync/utils/__init__.py +12 -6
- a_sync/utils/iterators.py +170 -50
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
- ez_a_sync-0.22.16.dist-info/RECORD +74 -0
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
- tests/conftest.py +1 -2
- tests/executor.py +250 -9
- tests/fixtures.py +61 -32
- tests/test_abstract.py +22 -4
- tests/test_as_completed.py +54 -21
- tests/test_base.py +264 -19
- tests/test_cache.py +31 -15
- tests/test_decorator.py +54 -28
- tests/test_executor.py +31 -13
- tests/test_future.py +45 -8
- tests/test_gather.py +8 -2
- tests/test_helpers.py +2 -0
- tests/test_iter.py +55 -13
- tests/test_limiter.py +5 -3
- tests/test_meta.py +23 -9
- tests/test_modified.py +4 -1
- tests/test_semaphore.py +15 -8
- tests/test_singleton.py +28 -11
- tests/test_task.py +162 -36
- ez_a_sync-0.22.14.dist-info/RECORD +0 -74
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/top_level.txt +0 -0
a_sync/a_sync/function.py
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
|
|
2
1
|
import functools
|
|
3
2
|
import inspect
|
|
4
3
|
import logging
|
|
5
4
|
import sys
|
|
6
5
|
|
|
7
6
|
from async_lru import _LRUCacheWrapper
|
|
8
|
-
from async_property.base import
|
|
9
|
-
|
|
10
|
-
from async_property.cached import \
|
|
11
|
-
AsyncCachedPropertyDescriptor # type: ignore [import]
|
|
7
|
+
from async_property.base import AsyncPropertyDescriptor # type: ignore [import]
|
|
8
|
+
from async_property.cached import AsyncCachedPropertyDescriptor # type: ignore [import]
|
|
12
9
|
|
|
13
10
|
from a_sync._typing import *
|
|
14
11
|
from a_sync.a_sync import _flags, _helpers, _kwargs
|
|
@@ -16,31 +13,47 @@ from a_sync.a_sync.modifiers.manager import ModifierManager
|
|
|
16
13
|
|
|
17
14
|
if TYPE_CHECKING:
|
|
18
15
|
from a_sync import TaskMapping
|
|
19
|
-
from a_sync.a_sync.method import (
|
|
20
|
-
|
|
16
|
+
from a_sync.a_sync.method import (
|
|
17
|
+
ASyncBoundMethod,
|
|
18
|
+
ASyncBoundMethodAsyncDefault,
|
|
19
|
+
ASyncBoundMethodSyncDefault,
|
|
20
|
+
)
|
|
21
21
|
|
|
22
22
|
logger = logging.getLogger(__name__)
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
class _ModifiedMixin:
|
|
25
26
|
"""
|
|
26
|
-
A mixin class that provides functionality for applying modifiers to functions.
|
|
27
|
+
A mixin class for internal use that provides functionality for applying modifiers to functions.
|
|
27
28
|
|
|
28
|
-
This class is used as a base for :class:`~ASyncFunction` and its variants
|
|
29
|
-
|
|
29
|
+
This class is used as a base for :class:`~ASyncFunction` and its variants, such as
|
|
30
|
+
`ASyncFunctionAsyncDefault` and `ASyncFunctionSyncDefault`, to handle the application
|
|
31
|
+
of async and sync modifiers to functions. Modifiers can alter the behavior of functions,
|
|
32
|
+
such as converting sync functions to async, applying caching, or rate limiting.
|
|
33
|
+
|
|
34
|
+
See Also:
|
|
35
|
+
- :class:`~ASyncFunction`
|
|
36
|
+
- :class:`~ModifierManager`
|
|
30
37
|
"""
|
|
31
38
|
|
|
39
|
+
# TODO: give me a docstring
|
|
32
40
|
modifiers: ModifierManager
|
|
41
|
+
|
|
33
42
|
__slots__ = "modifiers", "wrapped"
|
|
34
43
|
|
|
35
44
|
def _asyncify(self, func: SyncFn[P, T]) -> CoroFn[P, T]:
|
|
36
45
|
"""
|
|
37
|
-
|
|
46
|
+
Converts a synchronous function to an asynchronous one and applies async modifiers.
|
|
38
47
|
|
|
39
48
|
Args:
|
|
40
49
|
func: The synchronous function to be converted.
|
|
41
50
|
|
|
42
51
|
Returns:
|
|
43
|
-
|
|
52
|
+
The asynchronous version of the function with applied modifiers.
|
|
53
|
+
|
|
54
|
+
See Also:
|
|
55
|
+
- :func:`_helpers._asyncify`
|
|
56
|
+
- :meth:`ModifierManager.apply_async_modifiers`
|
|
44
57
|
"""
|
|
45
58
|
coro_fn = _helpers._asyncify(func, self.modifiers.executor)
|
|
46
59
|
return self.modifiers.apply_async_modifiers(coro_fn)
|
|
@@ -48,39 +61,60 @@ class ModifiedMixin:
|
|
|
48
61
|
@functools.cached_property
|
|
49
62
|
def _await(self) -> Callable[[Awaitable[T]], T]:
|
|
50
63
|
"""
|
|
51
|
-
|
|
64
|
+
Applies sync modifiers to the _helpers._await function and caches it.
|
|
52
65
|
|
|
53
66
|
Returns:
|
|
54
|
-
|
|
67
|
+
The modified _await function.
|
|
68
|
+
|
|
69
|
+
See Also:
|
|
70
|
+
- :func:`_helpers._await`
|
|
71
|
+
- :meth:`ModifierManager.apply_sync_modifiers`
|
|
55
72
|
"""
|
|
56
73
|
return self.modifiers.apply_sync_modifiers(_helpers._await)
|
|
57
74
|
|
|
58
75
|
@functools.cached_property
|
|
59
76
|
def default(self) -> DefaultMode:
|
|
60
77
|
"""
|
|
61
|
-
|
|
78
|
+
Gets the default execution mode (sync, async, or None) for the function.
|
|
62
79
|
|
|
63
80
|
Returns:
|
|
64
|
-
The default execution mode
|
|
81
|
+
The default execution mode.
|
|
82
|
+
|
|
83
|
+
See Also:
|
|
84
|
+
- :attr:`ModifierManager.default`
|
|
65
85
|
"""
|
|
66
86
|
return self.modifiers.default
|
|
67
87
|
|
|
68
88
|
|
|
69
89
|
def _validate_wrapped_fn(fn: Callable) -> None:
|
|
70
|
-
"""Ensures 'fn' is an appropriate function for wrapping with a_sync.
|
|
90
|
+
"""Ensures 'fn' is an appropriate function for wrapping with a_sync.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
fn: The function to validate.
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
TypeError: If the input is not callable.
|
|
97
|
+
RuntimeError: If the function has arguments with names that conflict with viable flags.
|
|
98
|
+
|
|
99
|
+
See Also:
|
|
100
|
+
- :func:`_check_not_genfunc`
|
|
101
|
+
"""
|
|
71
102
|
if isinstance(fn, (AsyncPropertyDescriptor, AsyncCachedPropertyDescriptor)):
|
|
72
|
-
return
|
|
103
|
+
return # These are always valid
|
|
73
104
|
if not callable(fn):
|
|
74
|
-
raise TypeError(f
|
|
105
|
+
raise TypeError(f"Input is not callable. Unable to decorate {fn}")
|
|
75
106
|
if isinstance(fn, _LRUCacheWrapper):
|
|
76
107
|
fn = fn.__wrapped__
|
|
77
108
|
_check_not_genfunc(fn)
|
|
78
109
|
fn_args = inspect.getfullargspec(fn)[0]
|
|
79
110
|
for flag in _flags.VIABLE_FLAGS:
|
|
80
111
|
if flag in fn_args:
|
|
81
|
-
raise RuntimeError(
|
|
112
|
+
raise RuntimeError(
|
|
113
|
+
f"{fn} must not have any arguments with the following names: {_flags.VIABLE_FLAGS}"
|
|
114
|
+
)
|
|
115
|
+
|
|
82
116
|
|
|
83
|
-
class ASyncFunction(
|
|
117
|
+
class ASyncFunction(_ModifiedMixin, Generic[P, T]):
|
|
84
118
|
"""
|
|
85
119
|
A callable wrapper object that can be executed both synchronously and asynchronously.
|
|
86
120
|
|
|
@@ -91,8 +125,12 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
91
125
|
The class supports various modifiers that can alter the behavior of the function,
|
|
92
126
|
such as caching, rate limiting, and execution in specific contexts (e.g., thread pools).
|
|
93
127
|
|
|
128
|
+
Note:
|
|
129
|
+
The logic for determining whether to execute the function synchronously or asynchronously
|
|
130
|
+
is handled by the `_run_sync` method, which checks for flags in the `kwargs` and defers
|
|
131
|
+
to the default execution mode if no flags are specified.
|
|
132
|
+
|
|
94
133
|
Example:
|
|
95
|
-
```python
|
|
96
134
|
async def my_coroutine(x: int) -> str:
|
|
97
135
|
return str(x)
|
|
98
136
|
|
|
@@ -103,22 +141,57 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
103
141
|
|
|
104
142
|
# Asynchronous call
|
|
105
143
|
result = await func(5) # returns "5"
|
|
106
|
-
|
|
144
|
+
|
|
145
|
+
See Also:
|
|
146
|
+
- :class:`_ModifiedMixin`
|
|
147
|
+
- :class:`ModifierManager`
|
|
107
148
|
"""
|
|
108
149
|
|
|
109
150
|
# NOTE: We can't use __slots__ here because it breaks functools.update_wrapper
|
|
110
151
|
|
|
111
152
|
@overload
|
|
112
|
-
def __init__(self, fn: CoroFn[P, T], **modifiers: Unpack[ModifierKwargs]) -> None
|
|
153
|
+
def __init__(self, fn: CoroFn[P, T], **modifiers: Unpack[ModifierKwargs]) -> None:
|
|
154
|
+
"""
|
|
155
|
+
Initializes an ASyncFunction instance for a coroutine function.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
fn: The coroutine function to wrap.
|
|
159
|
+
**modifiers: Keyword arguments for function modifiers.
|
|
160
|
+
|
|
161
|
+
Example:
|
|
162
|
+
async def my_coroutine(x: int) -> str:
|
|
163
|
+
return str(x)
|
|
164
|
+
|
|
165
|
+
func = ASyncFunction(my_coroutine, cache_type='memory')
|
|
166
|
+
"""
|
|
167
|
+
|
|
113
168
|
@overload
|
|
114
|
-
def __init__(self, fn: SyncFn[P, T], **modifiers: Unpack[ModifierKwargs]) -> None
|
|
169
|
+
def __init__(self, fn: SyncFn[P, T], **modifiers: Unpack[ModifierKwargs]) -> None:
|
|
170
|
+
"""
|
|
171
|
+
Initializes an ASyncFunction instance for a synchronous function.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
fn: The synchronous function to wrap.
|
|
175
|
+
**modifiers: Keyword arguments for function modifiers.
|
|
176
|
+
|
|
177
|
+
Example:
|
|
178
|
+
def my_function(x: int) -> str:
|
|
179
|
+
return str(x)
|
|
180
|
+
|
|
181
|
+
func = ASyncFunction(my_function, runs_per_minute=60)
|
|
182
|
+
"""
|
|
183
|
+
|
|
115
184
|
def __init__(self, fn: AnyFn[P, T], **modifiers: Unpack[ModifierKwargs]) -> None:
|
|
116
185
|
"""
|
|
117
|
-
|
|
186
|
+
Initializes an ASyncFunction instance.
|
|
118
187
|
|
|
119
188
|
Args:
|
|
120
189
|
fn: The function to wrap.
|
|
121
190
|
**modifiers: Keyword arguments for function modifiers.
|
|
191
|
+
|
|
192
|
+
See Also:
|
|
193
|
+
- :func:`_validate_wrapped_fn`
|
|
194
|
+
- :class:`ModifierManager`
|
|
122
195
|
"""
|
|
123
196
|
_validate_wrapped_fn(fn)
|
|
124
197
|
|
|
@@ -132,56 +205,143 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
132
205
|
if self.__doc__ is None:
|
|
133
206
|
self.__doc__ = f"Since `{self.__name__}` is an {self.__docstring_append__}"
|
|
134
207
|
else:
|
|
135
|
-
self.__doc__ +=
|
|
208
|
+
self.__doc__ += (
|
|
209
|
+
f"\n\nSince `{self.__name__}` is an {self.__docstring_append__}"
|
|
210
|
+
)
|
|
136
211
|
|
|
137
212
|
@overload
|
|
138
|
-
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T
|
|
213
|
+
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T:
|
|
214
|
+
"""
|
|
215
|
+
Calls the wrapped function synchronously.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
*args: Positional arguments to pass to the wrapped function.
|
|
219
|
+
sync: Must be True to indicate synchronous execution.
|
|
220
|
+
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
221
|
+
|
|
222
|
+
Example:
|
|
223
|
+
result = func(5, sync=True)
|
|
224
|
+
"""
|
|
225
|
+
|
|
139
226
|
@overload
|
|
140
|
-
def __call__(
|
|
227
|
+
def __call__(
|
|
228
|
+
self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs
|
|
229
|
+
) -> Coroutine[Any, Any, T]:
|
|
230
|
+
"""
|
|
231
|
+
Calls the wrapped function asynchronously.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
*args: Positional arguments to pass to the wrapped function.
|
|
235
|
+
sync: Must be False to indicate asynchronous execution.
|
|
236
|
+
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
237
|
+
|
|
238
|
+
Example:
|
|
239
|
+
result = await func(5, sync=False)
|
|
240
|
+
"""
|
|
241
|
+
|
|
141
242
|
@overload
|
|
142
|
-
def __call__(
|
|
243
|
+
def __call__(
|
|
244
|
+
self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs
|
|
245
|
+
) -> T:
|
|
246
|
+
"""
|
|
247
|
+
Calls the wrapped function synchronously.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
*args: Positional arguments to pass to the wrapped function.
|
|
251
|
+
asynchronous: Must be False to indicate synchronous execution.
|
|
252
|
+
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
253
|
+
|
|
254
|
+
Example:
|
|
255
|
+
result = func(5, asynchronous=False)
|
|
256
|
+
"""
|
|
257
|
+
|
|
143
258
|
@overload
|
|
144
|
-
def __call__(
|
|
259
|
+
def __call__(
|
|
260
|
+
self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs
|
|
261
|
+
) -> Coroutine[Any, Any, T]:
|
|
262
|
+
"""
|
|
263
|
+
Calls the wrapped function asynchronously.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
*args: Positional arguments to pass to the wrapped function.
|
|
267
|
+
asynchronous: Must be True to indicate asynchronous execution.
|
|
268
|
+
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
269
|
+
|
|
270
|
+
Example:
|
|
271
|
+
result = await func(5, asynchronous=True)
|
|
272
|
+
"""
|
|
273
|
+
|
|
145
274
|
@overload
|
|
146
|
-
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> MaybeCoro[T]:...
|
|
147
275
|
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> MaybeCoro[T]:
|
|
148
276
|
"""
|
|
149
|
-
|
|
277
|
+
Calls the wrapped function using the default execution mode.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
*args: Positional arguments to pass to the wrapped function.
|
|
281
|
+
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
282
|
+
|
|
283
|
+
Example:
|
|
284
|
+
result = func(5)
|
|
285
|
+
"""
|
|
286
|
+
|
|
287
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> MaybeCoro[T]:
|
|
288
|
+
"""
|
|
289
|
+
Calls the wrapped function either synchronously or asynchronously.
|
|
150
290
|
|
|
151
291
|
This method determines whether to execute the wrapped function synchronously
|
|
152
292
|
or asynchronously based on the default mode and any provided flags.
|
|
153
293
|
|
|
294
|
+
Note:
|
|
295
|
+
The logic for determining the execution mode is handled by the
|
|
296
|
+
:meth:`_run_sync` method, which checks for flags in the kwargs and
|
|
297
|
+
defers to the default execution mode if no flags are specified.
|
|
298
|
+
|
|
154
299
|
Args:
|
|
155
300
|
*args: Positional arguments to pass to the wrapped function.
|
|
156
301
|
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
157
302
|
|
|
158
|
-
Returns:
|
|
159
|
-
The result of the wrapped function call, which may be a coroutine if run asynchronously.
|
|
160
|
-
|
|
161
303
|
Raises:
|
|
162
304
|
Exception: Any exception that may be raised by the wrapped function.
|
|
305
|
+
|
|
306
|
+
See Also:
|
|
307
|
+
- :attr:`default`
|
|
308
|
+
- :meth:`_run_sync`
|
|
163
309
|
"""
|
|
164
|
-
logger.debug(
|
|
310
|
+
logger.debug(
|
|
311
|
+
"calling %s fn: %s with args: %s kwargs: %s", self, self.fn, args, kwargs
|
|
312
|
+
)
|
|
165
313
|
return self.fn(*args, **kwargs)
|
|
166
314
|
|
|
167
315
|
def __repr__(self) -> str:
|
|
168
316
|
return f"<{self.__class__.__name__} {self.__module__}.{self.__name__} at {hex(id(self))}>"
|
|
169
317
|
|
|
170
318
|
@functools.cached_property
|
|
171
|
-
def fn(self):
|
|
319
|
+
def fn(self):
|
|
320
|
+
# NOTE type hint doesnt work in py3.8 or py3.9, debug later
|
|
321
|
+
# -> Union[SyncFn[[CoroFn[P, T]], MaybeAwaitable[T]], SyncFn[[SyncFn[P, T]], MaybeAwaitable[T]]]:
|
|
172
322
|
"""
|
|
173
323
|
Returns the final wrapped version of :attr:`ASyncFunction._fn` decorated with all of the a_sync goodness.
|
|
174
324
|
|
|
175
325
|
Returns:
|
|
176
326
|
The final wrapped function.
|
|
327
|
+
|
|
328
|
+
See Also:
|
|
329
|
+
- :meth:`_async_wrap`
|
|
330
|
+
- :meth:`_sync_wrap`
|
|
177
331
|
"""
|
|
178
332
|
return self._async_wrap if self._async_def else self._sync_wrap
|
|
179
333
|
|
|
180
334
|
if sys.version_info >= (3, 11) or TYPE_CHECKING:
|
|
181
335
|
# we can specify P.args in python>=3.11 but in lower versions it causes a crash. Everything should still type check correctly on all versions.
|
|
182
|
-
def map(
|
|
336
|
+
def map(
|
|
337
|
+
self,
|
|
338
|
+
*iterables: AnyIterable[P.args],
|
|
339
|
+
concurrency: Optional[int] = None,
|
|
340
|
+
task_name: str = "",
|
|
341
|
+
**function_kwargs: P.kwargs,
|
|
342
|
+
) -> "TaskMapping[P, T]":
|
|
183
343
|
"""
|
|
184
|
-
|
|
344
|
+
Creates a TaskMapping for the wrapped function with the given iterables.
|
|
185
345
|
|
|
186
346
|
Args:
|
|
187
347
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -190,14 +350,30 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
190
350
|
**function_kwargs: Additional keyword arguments to pass to the function.
|
|
191
351
|
|
|
192
352
|
Returns:
|
|
193
|
-
A TaskMapping object.
|
|
353
|
+
A TaskMapping object for managing concurrent execution.
|
|
354
|
+
|
|
355
|
+
See Also:
|
|
356
|
+
- :class:`TaskMapping`
|
|
194
357
|
"""
|
|
195
358
|
from a_sync import TaskMapping
|
|
196
|
-
return TaskMapping(self, *iterables, concurrency=concurrency, name=task_name, **function_kwargs)
|
|
197
359
|
|
|
198
|
-
|
|
360
|
+
return TaskMapping(
|
|
361
|
+
self,
|
|
362
|
+
*iterables,
|
|
363
|
+
concurrency=concurrency,
|
|
364
|
+
name=task_name,
|
|
365
|
+
**function_kwargs,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
async def any(
|
|
369
|
+
self,
|
|
370
|
+
*iterables: AnyIterable[P.args],
|
|
371
|
+
concurrency: Optional[int] = None,
|
|
372
|
+
task_name: str = "",
|
|
373
|
+
**function_kwargs: P.kwargs,
|
|
374
|
+
) -> bool:
|
|
199
375
|
"""
|
|
200
|
-
|
|
376
|
+
Checks if any result of the function applied to the iterables is truthy.
|
|
201
377
|
|
|
202
378
|
Args:
|
|
203
379
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -206,13 +382,27 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
206
382
|
**function_kwargs: Additional keyword arguments to pass to the function.
|
|
207
383
|
|
|
208
384
|
Returns:
|
|
209
|
-
|
|
210
|
-
"""
|
|
211
|
-
return await self.map(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).any(pop=True, sync=False)
|
|
385
|
+
True if any result is truthy, otherwise False.
|
|
212
386
|
|
|
213
|
-
|
|
387
|
+
See Also:
|
|
388
|
+
- :meth:`map`
|
|
214
389
|
"""
|
|
215
|
-
|
|
390
|
+
return await self.map(
|
|
391
|
+
*iterables,
|
|
392
|
+
concurrency=concurrency,
|
|
393
|
+
task_name=task_name,
|
|
394
|
+
**function_kwargs,
|
|
395
|
+
).any(pop=True, sync=False)
|
|
396
|
+
|
|
397
|
+
async def all(
|
|
398
|
+
self,
|
|
399
|
+
*iterables: AnyIterable[P.args],
|
|
400
|
+
concurrency: Optional[int] = None,
|
|
401
|
+
task_name: str = "",
|
|
402
|
+
**function_kwargs: P.kwargs,
|
|
403
|
+
) -> bool:
|
|
404
|
+
"""
|
|
405
|
+
Checks if all results of the function applied to the iterables are truthy.
|
|
216
406
|
|
|
217
407
|
Args:
|
|
218
408
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -221,13 +411,27 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
221
411
|
**function_kwargs: Additional keyword arguments to pass to the function.
|
|
222
412
|
|
|
223
413
|
Returns:
|
|
224
|
-
|
|
225
|
-
"""
|
|
226
|
-
return await self.map(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).all(pop=True, sync=False)
|
|
414
|
+
True if all results are truthy, otherwise False.
|
|
227
415
|
|
|
228
|
-
|
|
416
|
+
See Also:
|
|
417
|
+
- :meth:`map`
|
|
229
418
|
"""
|
|
230
|
-
|
|
419
|
+
return await self.map(
|
|
420
|
+
*iterables,
|
|
421
|
+
concurrency=concurrency,
|
|
422
|
+
task_name=task_name,
|
|
423
|
+
**function_kwargs,
|
|
424
|
+
).all(pop=True, sync=False)
|
|
425
|
+
|
|
426
|
+
async def min(
|
|
427
|
+
self,
|
|
428
|
+
*iterables: AnyIterable[P.args],
|
|
429
|
+
concurrency: Optional[int] = None,
|
|
430
|
+
task_name: str = "",
|
|
431
|
+
**function_kwargs: P.kwargs,
|
|
432
|
+
) -> T:
|
|
433
|
+
"""
|
|
434
|
+
Finds the minimum result of the function applied to the iterables.
|
|
231
435
|
|
|
232
436
|
Args:
|
|
233
437
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -237,12 +441,26 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
237
441
|
|
|
238
442
|
Returns:
|
|
239
443
|
The minimum result.
|
|
240
|
-
"""
|
|
241
|
-
return await self.map(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).min(pop=True, sync=False)
|
|
242
444
|
|
|
243
|
-
|
|
445
|
+
See Also:
|
|
446
|
+
- :meth:`map`
|
|
244
447
|
"""
|
|
245
|
-
|
|
448
|
+
return await self.map(
|
|
449
|
+
*iterables,
|
|
450
|
+
concurrency=concurrency,
|
|
451
|
+
task_name=task_name,
|
|
452
|
+
**function_kwargs,
|
|
453
|
+
).min(pop=True, sync=False)
|
|
454
|
+
|
|
455
|
+
async def max(
|
|
456
|
+
self,
|
|
457
|
+
*iterables: AnyIterable[P.args],
|
|
458
|
+
concurrency: Optional[int] = None,
|
|
459
|
+
task_name: str = "",
|
|
460
|
+
**function_kwargs: P.kwargs,
|
|
461
|
+
) -> T:
|
|
462
|
+
"""
|
|
463
|
+
Finds the maximum result of the function applied to the iterables.
|
|
246
464
|
|
|
247
465
|
Args:
|
|
248
466
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -252,12 +470,26 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
252
470
|
|
|
253
471
|
Returns:
|
|
254
472
|
The maximum result.
|
|
255
|
-
"""
|
|
256
|
-
return await self.map(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).max(pop=True, sync=False)
|
|
257
473
|
|
|
258
|
-
|
|
474
|
+
See Also:
|
|
475
|
+
- :meth:`map`
|
|
259
476
|
"""
|
|
260
|
-
|
|
477
|
+
return await self.map(
|
|
478
|
+
*iterables,
|
|
479
|
+
concurrency=concurrency,
|
|
480
|
+
task_name=task_name,
|
|
481
|
+
**function_kwargs,
|
|
482
|
+
).max(pop=True, sync=False)
|
|
483
|
+
|
|
484
|
+
async def sum(
|
|
485
|
+
self,
|
|
486
|
+
*iterables: AnyIterable[P.args],
|
|
487
|
+
concurrency: Optional[int] = None,
|
|
488
|
+
task_name: str = "",
|
|
489
|
+
**function_kwargs: P.kwargs,
|
|
490
|
+
) -> T:
|
|
491
|
+
"""
|
|
492
|
+
Calculates the sum of the results of the function applied to the iterables.
|
|
261
493
|
|
|
262
494
|
Args:
|
|
263
495
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -267,12 +499,28 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
267
499
|
|
|
268
500
|
Returns:
|
|
269
501
|
The sum of the results.
|
|
502
|
+
|
|
503
|
+
See Also:
|
|
504
|
+
- :meth:`map`
|
|
270
505
|
"""
|
|
271
|
-
return await self.map(
|
|
506
|
+
return await self.map(
|
|
507
|
+
*iterables,
|
|
508
|
+
concurrency=concurrency,
|
|
509
|
+
task_name=task_name,
|
|
510
|
+
**function_kwargs,
|
|
511
|
+
).sum(pop=True, sync=False)
|
|
512
|
+
|
|
272
513
|
else:
|
|
273
|
-
|
|
514
|
+
|
|
515
|
+
def map(
|
|
516
|
+
self,
|
|
517
|
+
*iterables: AnyIterable[Any],
|
|
518
|
+
concurrency: Optional[int] = None,
|
|
519
|
+
task_name: str = "",
|
|
520
|
+
**function_kwargs: P.kwargs,
|
|
521
|
+
) -> "TaskMapping[P, T]":
|
|
274
522
|
"""
|
|
275
|
-
|
|
523
|
+
Creates a TaskMapping for the wrapped function with the given iterables.
|
|
276
524
|
|
|
277
525
|
Args:
|
|
278
526
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -281,14 +529,30 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
281
529
|
**function_kwargs: Additional keyword arguments to pass to the function.
|
|
282
530
|
|
|
283
531
|
Returns:
|
|
284
|
-
A TaskMapping object.
|
|
532
|
+
A TaskMapping object for managing concurrent execution.
|
|
533
|
+
|
|
534
|
+
See Also:
|
|
535
|
+
- :class:`TaskMapping`
|
|
285
536
|
"""
|
|
286
537
|
from a_sync import TaskMapping
|
|
287
|
-
return TaskMapping(self, *iterables, concurrency=concurrency, name=task_name, **function_kwargs)
|
|
288
538
|
|
|
289
|
-
|
|
539
|
+
return TaskMapping(
|
|
540
|
+
self,
|
|
541
|
+
*iterables,
|
|
542
|
+
concurrency=concurrency,
|
|
543
|
+
name=task_name,
|
|
544
|
+
**function_kwargs,
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
async def any(
|
|
548
|
+
self,
|
|
549
|
+
*iterables: AnyIterable[Any],
|
|
550
|
+
concurrency: Optional[int] = None,
|
|
551
|
+
task_name: str = "",
|
|
552
|
+
**function_kwargs: P.kwargs,
|
|
553
|
+
) -> bool:
|
|
290
554
|
"""
|
|
291
|
-
|
|
555
|
+
Checks if any result of the function applied to the iterables is truthy.
|
|
292
556
|
|
|
293
557
|
Args:
|
|
294
558
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -297,13 +561,27 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
297
561
|
**function_kwargs: Additional keyword arguments to pass to the function.
|
|
298
562
|
|
|
299
563
|
Returns:
|
|
300
|
-
|
|
301
|
-
"""
|
|
302
|
-
return await self.map(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).any(pop=True, sync=False)
|
|
564
|
+
True if any result is truthy, otherwise False.
|
|
303
565
|
|
|
304
|
-
|
|
566
|
+
See Also:
|
|
567
|
+
- :meth:`map`
|
|
305
568
|
"""
|
|
306
|
-
|
|
569
|
+
return await self.map(
|
|
570
|
+
*iterables,
|
|
571
|
+
concurrency=concurrency,
|
|
572
|
+
task_name=task_name,
|
|
573
|
+
**function_kwargs,
|
|
574
|
+
).any(pop=True, sync=False)
|
|
575
|
+
|
|
576
|
+
async def all(
|
|
577
|
+
self,
|
|
578
|
+
*iterables: AnyIterable[Any],
|
|
579
|
+
concurrency: Optional[int] = None,
|
|
580
|
+
task_name: str = "",
|
|
581
|
+
**function_kwargs: P.kwargs,
|
|
582
|
+
) -> bool:
|
|
583
|
+
"""
|
|
584
|
+
Checks if all results of the function applied to the iterables are truthy.
|
|
307
585
|
|
|
308
586
|
Args:
|
|
309
587
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -312,13 +590,27 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
312
590
|
**function_kwargs: Additional keyword arguments to pass to the function.
|
|
313
591
|
|
|
314
592
|
Returns:
|
|
315
|
-
|
|
316
|
-
"""
|
|
317
|
-
return await self.map(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).all(pop=True, sync=False)
|
|
593
|
+
True if all results are truthy, otherwise False.
|
|
318
594
|
|
|
319
|
-
|
|
595
|
+
See Also:
|
|
596
|
+
- :meth:`map`
|
|
320
597
|
"""
|
|
321
|
-
|
|
598
|
+
return await self.map(
|
|
599
|
+
*iterables,
|
|
600
|
+
concurrency=concurrency,
|
|
601
|
+
task_name=task_name,
|
|
602
|
+
**function_kwargs,
|
|
603
|
+
).all(pop=True, sync=False)
|
|
604
|
+
|
|
605
|
+
async def min(
|
|
606
|
+
self,
|
|
607
|
+
*iterables: AnyIterable[Any],
|
|
608
|
+
concurrency: Optional[int] = None,
|
|
609
|
+
task_name: str = "",
|
|
610
|
+
**function_kwargs: P.kwargs,
|
|
611
|
+
) -> T:
|
|
612
|
+
"""
|
|
613
|
+
Finds the minimum result of the function applied to the iterables.
|
|
322
614
|
|
|
323
615
|
Args:
|
|
324
616
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -328,12 +620,26 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
328
620
|
|
|
329
621
|
Returns:
|
|
330
622
|
The minimum result.
|
|
331
|
-
"""
|
|
332
|
-
return await self.map(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).min(pop=True, sync=False)
|
|
333
623
|
|
|
334
|
-
|
|
624
|
+
See Also:
|
|
625
|
+
- :meth:`map`
|
|
335
626
|
"""
|
|
336
|
-
|
|
627
|
+
return await self.map(
|
|
628
|
+
*iterables,
|
|
629
|
+
concurrency=concurrency,
|
|
630
|
+
task_name=task_name,
|
|
631
|
+
**function_kwargs,
|
|
632
|
+
).min(pop=True, sync=False)
|
|
633
|
+
|
|
634
|
+
async def max(
|
|
635
|
+
self,
|
|
636
|
+
*iterables: AnyIterable[Any],
|
|
637
|
+
concurrency: Optional[int] = None,
|
|
638
|
+
task_name: str = "",
|
|
639
|
+
**function_kwargs: P.kwargs,
|
|
640
|
+
) -> T:
|
|
641
|
+
"""
|
|
642
|
+
Finds the maximum result of the function applied to the iterables.
|
|
337
643
|
|
|
338
644
|
Args:
|
|
339
645
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -343,12 +649,26 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
343
649
|
|
|
344
650
|
Returns:
|
|
345
651
|
The maximum result.
|
|
346
|
-
"""
|
|
347
|
-
return await self.map(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).max(pop=True, sync=False)
|
|
348
652
|
|
|
349
|
-
|
|
653
|
+
See Also:
|
|
654
|
+
- :meth:`map`
|
|
350
655
|
"""
|
|
351
|
-
|
|
656
|
+
return await self.map(
|
|
657
|
+
*iterables,
|
|
658
|
+
concurrency=concurrency,
|
|
659
|
+
task_name=task_name,
|
|
660
|
+
**function_kwargs,
|
|
661
|
+
).max(pop=True, sync=False)
|
|
662
|
+
|
|
663
|
+
async def sum(
|
|
664
|
+
self,
|
|
665
|
+
*iterables: AnyIterable[Any],
|
|
666
|
+
concurrency: Optional[int] = None,
|
|
667
|
+
task_name: str = "",
|
|
668
|
+
**function_kwargs: P.kwargs,
|
|
669
|
+
) -> T:
|
|
670
|
+
"""
|
|
671
|
+
Calculates the sum of the results of the function applied to the iterables.
|
|
352
672
|
|
|
353
673
|
Args:
|
|
354
674
|
*iterables: Iterable objects to be used as arguments for the function.
|
|
@@ -358,35 +678,53 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
358
678
|
|
|
359
679
|
Returns:
|
|
360
680
|
The sum of the results.
|
|
681
|
+
|
|
682
|
+
See Also:
|
|
683
|
+
- :meth:`map`
|
|
361
684
|
"""
|
|
362
|
-
return await self.map(
|
|
685
|
+
return await self.map(
|
|
686
|
+
*iterables,
|
|
687
|
+
concurrency=concurrency,
|
|
688
|
+
task_name=task_name,
|
|
689
|
+
**function_kwargs,
|
|
690
|
+
).sum(pop=True, sync=False)
|
|
363
691
|
|
|
364
692
|
@functools.cached_property
|
|
365
693
|
def _sync_default(self) -> bool:
|
|
366
694
|
"""
|
|
367
|
-
|
|
695
|
+
Determines the default execution mode (sync or async) for the function.
|
|
368
696
|
|
|
369
697
|
If the user did not specify a default, this method defers to the function's
|
|
370
698
|
definition (sync vs async def).
|
|
371
699
|
|
|
372
700
|
Returns:
|
|
373
|
-
True if the default is sync, False if
|
|
701
|
+
True if the default is sync, False if async.
|
|
702
|
+
|
|
703
|
+
See Also:
|
|
704
|
+
- :attr:`default`
|
|
374
705
|
"""
|
|
375
|
-
return
|
|
706
|
+
return (
|
|
707
|
+
True
|
|
708
|
+
if self.default == "sync"
|
|
709
|
+
else False if self.default == "async" else not self._async_def
|
|
710
|
+
)
|
|
376
711
|
|
|
377
712
|
@functools.cached_property
|
|
378
713
|
def _async_def(self) -> bool:
|
|
379
714
|
"""
|
|
380
|
-
|
|
715
|
+
Checks if the wrapped function is an asynchronous function.
|
|
381
716
|
|
|
382
717
|
Returns:
|
|
383
|
-
True if the
|
|
718
|
+
True if the function is asynchronous, otherwise False.
|
|
719
|
+
|
|
720
|
+
See Also:
|
|
721
|
+
- :func:`asyncio.iscoroutinefunction`
|
|
384
722
|
"""
|
|
385
723
|
return asyncio.iscoroutinefunction(self.__wrapped__)
|
|
386
724
|
|
|
387
725
|
def _run_sync(self, kwargs: dict) -> bool:
|
|
388
726
|
"""
|
|
389
|
-
|
|
727
|
+
Determines whether to run the function synchronously or asynchronously.
|
|
390
728
|
|
|
391
729
|
This method checks for a flag in the kwargs and defers to it if present.
|
|
392
730
|
If no flag is specified, it defers to the default execution mode.
|
|
@@ -395,7 +733,11 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
395
733
|
kwargs: The keyword arguments passed to the function.
|
|
396
734
|
|
|
397
735
|
Returns:
|
|
398
|
-
True if the function should
|
|
736
|
+
True if the function should run synchronously, otherwise False.
|
|
737
|
+
|
|
738
|
+
See Also:
|
|
739
|
+
- :func:`_kwargs.get_flag_name`
|
|
740
|
+
- :func:`_kwargs.is_sync`
|
|
399
741
|
"""
|
|
400
742
|
if flag := _kwargs.get_flag_name(kwargs):
|
|
401
743
|
# If a flag was specified in the kwargs, we will defer to it.
|
|
@@ -407,88 +749,175 @@ class ASyncFunction(ModifiedMixin, Generic[P, T]):
|
|
|
407
749
|
@functools.cached_property
|
|
408
750
|
def _asyncified(self) -> CoroFn[P, T]:
|
|
409
751
|
"""
|
|
410
|
-
|
|
752
|
+
Converts the wrapped function to an asynchronous function and applies both sync and async modifiers.
|
|
753
|
+
|
|
754
|
+
Raises:
|
|
755
|
+
TypeError: If the wrapped function is already asynchronous.
|
|
411
756
|
|
|
412
757
|
Returns:
|
|
413
|
-
|
|
758
|
+
The asynchronous version of the wrapped function.
|
|
759
|
+
|
|
760
|
+
See Also:
|
|
761
|
+
- :meth:`_asyncify`
|
|
414
762
|
"""
|
|
415
763
|
if self._async_def:
|
|
416
|
-
raise TypeError(
|
|
764
|
+
raise TypeError(
|
|
765
|
+
f"Can only be applied to sync functions, not {self.__wrapped__}"
|
|
766
|
+
)
|
|
417
767
|
return self._asyncify(self._modified_fn) # type: ignore [arg-type]
|
|
418
768
|
|
|
419
769
|
@functools.cached_property
|
|
420
770
|
def _modified_fn(self) -> AnyFn[P, T]:
|
|
421
771
|
"""
|
|
422
|
-
|
|
772
|
+
Applies modifiers to the wrapped function.
|
|
423
773
|
|
|
424
774
|
If the wrapped function is an asynchronous function, this method applies async modifiers.
|
|
425
775
|
If the wrapped function is a synchronous function, this method applies sync modifiers.
|
|
426
776
|
|
|
427
777
|
Returns:
|
|
428
|
-
The
|
|
778
|
+
The modified function.
|
|
779
|
+
|
|
780
|
+
See Also:
|
|
781
|
+
- :meth:`ModifierManager.apply_async_modifiers`
|
|
782
|
+
- :meth:`ModifierManager.apply_sync_modifiers`
|
|
429
783
|
"""
|
|
430
784
|
if self._async_def:
|
|
431
785
|
return self.modifiers.apply_async_modifiers(self.__wrapped__) # type: ignore [arg-type]
|
|
432
786
|
return self.modifiers.apply_sync_modifiers(self.__wrapped__) # type: ignore [return-value]
|
|
433
787
|
|
|
434
788
|
@functools.cached_property
|
|
435
|
-
def _async_wrap(self):
|
|
789
|
+
def _async_wrap(self): # -> SyncFn[[CoroFn[P, T]], MaybeAwaitable[T]]:
|
|
436
790
|
"""
|
|
437
791
|
The final wrapper if the wrapped function is an asynchronous function.
|
|
438
792
|
|
|
439
793
|
This method applies the appropriate modifiers and determines whether to await the result.
|
|
440
794
|
|
|
441
795
|
Returns:
|
|
442
|
-
The
|
|
796
|
+
The wrapped function with async handling.
|
|
797
|
+
|
|
798
|
+
See Also:
|
|
799
|
+
- :meth:`_run_sync`
|
|
800
|
+
- :meth:`_await`
|
|
443
801
|
"""
|
|
802
|
+
|
|
444
803
|
@functools.wraps(self._modified_fn)
|
|
445
804
|
def async_wrap(*args: P.args, **kwargs: P.kwargs) -> MaybeAwaitable[T]: # type: ignore [name-defined]
|
|
446
|
-
should_await = self._run_sync(
|
|
805
|
+
should_await = self._run_sync(
|
|
806
|
+
kwargs
|
|
807
|
+
) # Must take place before coro is created, we're popping a kwarg.
|
|
447
808
|
coro = self._modified_fn(*args, **kwargs)
|
|
448
809
|
return self._await(coro) if should_await else coro
|
|
810
|
+
|
|
449
811
|
return async_wrap
|
|
450
812
|
|
|
451
813
|
@functools.cached_property
|
|
452
|
-
def _sync_wrap(self):
|
|
814
|
+
def _sync_wrap(self): # -> SyncFn[[SyncFn[P, T]], MaybeAwaitable[T]]:
|
|
453
815
|
"""
|
|
454
816
|
The final wrapper if the wrapped function is a synchronous function.
|
|
455
817
|
|
|
456
818
|
This method applies the appropriate modifiers and determines whether to run the function synchronously or asynchronously.
|
|
457
819
|
|
|
458
820
|
Returns:
|
|
459
|
-
The
|
|
821
|
+
The wrapped function with sync handling.
|
|
822
|
+
|
|
823
|
+
See Also:
|
|
824
|
+
- :meth:`_run_sync`
|
|
825
|
+
- :meth:`_asyncified`
|
|
460
826
|
"""
|
|
827
|
+
|
|
461
828
|
@functools.wraps(self._modified_fn)
|
|
462
829
|
def sync_wrap(*args: P.args, **kwargs: P.kwargs) -> MaybeAwaitable[T]: # type: ignore [name-defined]
|
|
463
830
|
if self._run_sync(kwargs):
|
|
464
831
|
return self._modified_fn(*args, **kwargs)
|
|
465
|
-
return self._asyncified(*args, **kwargs)
|
|
832
|
+
return self._asyncified(*args, **kwargs)
|
|
833
|
+
|
|
466
834
|
return sync_wrap
|
|
467
835
|
|
|
468
836
|
__docstring_append__ = ":class:`~a_sync.a_sync.function.ASyncFunction`, you can optionally pass either a `sync` or `asynchronous` kwarg with a boolean value."
|
|
469
837
|
|
|
838
|
+
|
|
470
839
|
if sys.version_info < (3, 10):
|
|
471
840
|
_inherit = ASyncFunction[AnyFn[P, T], ASyncFunction[P, T]]
|
|
472
841
|
else:
|
|
473
842
|
_inherit = ASyncFunction[[AnyFn[P, T]], ASyncFunction[P, T]]
|
|
474
|
-
|
|
475
|
-
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
class ASyncDecorator(_ModifiedMixin):
|
|
476
846
|
def __init__(self, **modifiers: Unpack[ModifierKwargs]) -> None:
|
|
477
|
-
|
|
847
|
+
"""
|
|
848
|
+
Initializes an ASyncDecorator instance.
|
|
849
|
+
|
|
850
|
+
Args:
|
|
851
|
+
**modifiers: Keyword arguments for function modifiers.
|
|
852
|
+
|
|
853
|
+
Raises:
|
|
854
|
+
ValueError: If 'default' is not 'sync', 'async', or None.
|
|
855
|
+
|
|
856
|
+
See Also:
|
|
857
|
+
- :class:`ModifierManager`
|
|
858
|
+
"""
|
|
859
|
+
assert "default" in modifiers, modifiers
|
|
478
860
|
self.modifiers = ModifierManager(modifiers)
|
|
479
861
|
self.validate_inputs()
|
|
480
|
-
|
|
862
|
+
|
|
481
863
|
def validate_inputs(self) -> None:
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
864
|
+
"""
|
|
865
|
+
Validates the input modifiers.
|
|
866
|
+
|
|
867
|
+
Raises:
|
|
868
|
+
ValueError: If 'default' is not 'sync', 'async', or None.
|
|
869
|
+
|
|
870
|
+
See Also:
|
|
871
|
+
- :attr:`ModifierManager.default`
|
|
872
|
+
"""
|
|
873
|
+
if self.modifiers.default not in ["sync", "async", None]:
|
|
874
|
+
raise ValueError(
|
|
875
|
+
f"'default' must be either 'sync', 'async', or None. You passed {self.modifiers.default}."
|
|
876
|
+
)
|
|
877
|
+
|
|
485
878
|
@overload
|
|
486
879
|
def __call__(self, func: AnyFn[Concatenate[B, P], T]) -> "ASyncBoundMethod[B, P, T]": # type: ignore [override]
|
|
487
|
-
|
|
880
|
+
"""
|
|
881
|
+
Decorates a bound method with async or sync behavior based on the default modifier.
|
|
882
|
+
|
|
883
|
+
Args:
|
|
884
|
+
func: The bound method to decorate.
|
|
885
|
+
|
|
886
|
+
Returns:
|
|
887
|
+
An ASyncBoundMethod instance with the appropriate default behavior.
|
|
888
|
+
|
|
889
|
+
See Also:
|
|
890
|
+
- :class:`ASyncBoundMethod`
|
|
891
|
+
"""
|
|
892
|
+
|
|
488
893
|
@overload
|
|
489
894
|
def __call__(self, func: AnyFn[P, T]) -> ASyncFunction[P, T]: # type: ignore [override]
|
|
490
|
-
|
|
895
|
+
"""
|
|
896
|
+
Decorates a function with async or sync behavior based on the default modifier.
|
|
897
|
+
|
|
898
|
+
Args:
|
|
899
|
+
func: The function to decorate.
|
|
900
|
+
|
|
901
|
+
Returns:
|
|
902
|
+
An ASyncFunction instance with the appropriate default behavior.
|
|
903
|
+
|
|
904
|
+
See Also:
|
|
905
|
+
- :class:`ASyncFunction`
|
|
906
|
+
"""
|
|
907
|
+
|
|
491
908
|
def __call__(self, func: AnyFn[P, T]) -> ASyncFunction[P, T]: # type: ignore [override]
|
|
909
|
+
"""
|
|
910
|
+
Decorates a function with async or sync behavior based on the default modifier.
|
|
911
|
+
|
|
912
|
+
Args:
|
|
913
|
+
func: The function to decorate.
|
|
914
|
+
|
|
915
|
+
Returns:
|
|
916
|
+
An ASyncFunction instance with the appropriate default behavior.
|
|
917
|
+
|
|
918
|
+
See Also:
|
|
919
|
+
- :class:`ASyncFunction`
|
|
920
|
+
"""
|
|
492
921
|
if self.default == "async":
|
|
493
922
|
return ASyncFunctionAsyncDefault(func, **self.modifiers)
|
|
494
923
|
elif self.default == "sync":
|
|
@@ -498,13 +927,27 @@ class ASyncDecorator(ModifiedMixin):
|
|
|
498
927
|
else:
|
|
499
928
|
return ASyncFunctionSyncDefault(func, **self.modifiers)
|
|
500
929
|
|
|
930
|
+
|
|
501
931
|
def _check_not_genfunc(func: Callable) -> None:
|
|
932
|
+
"""Raises an error if the function is a generator or async generator.
|
|
933
|
+
|
|
934
|
+
Args:
|
|
935
|
+
func: The function to check.
|
|
936
|
+
|
|
937
|
+
Raises:
|
|
938
|
+
ValueError: If the function is a generator or async generator.
|
|
939
|
+
|
|
940
|
+
See Also:
|
|
941
|
+
- :func:`inspect.isasyncgenfunction`
|
|
942
|
+
- :func:`inspect.isgeneratorfunction`
|
|
943
|
+
"""
|
|
502
944
|
if inspect.isasyncgenfunction(func) or inspect.isgeneratorfunction(func):
|
|
503
945
|
raise ValueError("unable to decorate generator functions with this decorator")
|
|
504
946
|
|
|
505
947
|
|
|
506
948
|
# Mypy helper classes
|
|
507
949
|
|
|
950
|
+
|
|
508
951
|
class ASyncFunctionSyncDefault(ASyncFunction[P, T]):
|
|
509
952
|
"""A specialized :class:`~ASyncFunction` that defaults to synchronous execution.
|
|
510
953
|
|
|
@@ -516,7 +959,6 @@ class ASyncFunctionSyncDefault(ASyncFunction[P, T]):
|
|
|
516
959
|
or `asynchronous=True` as a keyword argument.
|
|
517
960
|
|
|
518
961
|
Example:
|
|
519
|
-
```python
|
|
520
962
|
@a_sync(default='sync')
|
|
521
963
|
async def my_function(x: int) -> str:
|
|
522
964
|
return str(x)
|
|
@@ -526,20 +968,41 @@ class ASyncFunctionSyncDefault(ASyncFunction[P, T]):
|
|
|
526
968
|
|
|
527
969
|
# Asynchronous call
|
|
528
970
|
result = await my_function(5, sync=False) # returns "5"
|
|
529
|
-
```
|
|
530
971
|
"""
|
|
972
|
+
|
|
531
973
|
@overload
|
|
532
|
-
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T
|
|
974
|
+
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T:
|
|
975
|
+
# TODO write specific docs for this overload
|
|
976
|
+
...
|
|
977
|
+
|
|
533
978
|
@overload
|
|
534
|
-
def __call__(
|
|
979
|
+
def __call__(
|
|
980
|
+
self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs
|
|
981
|
+
) -> Coroutine[Any, Any, T]:
|
|
982
|
+
# TODO write specific docs for this overload
|
|
983
|
+
...
|
|
984
|
+
|
|
535
985
|
@overload
|
|
536
|
-
def __call__(
|
|
986
|
+
def __call__(
|
|
987
|
+
self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs
|
|
988
|
+
) -> T:
|
|
989
|
+
# TODO write specific docs for this overload
|
|
990
|
+
...
|
|
991
|
+
|
|
537
992
|
@overload
|
|
538
|
-
def __call__(
|
|
993
|
+
def __call__(
|
|
994
|
+
self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs
|
|
995
|
+
) -> Coroutine[Any, Any, T]:
|
|
996
|
+
# TODO write specific docs for this overload
|
|
997
|
+
...
|
|
998
|
+
|
|
539
999
|
@overload
|
|
540
|
-
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T
|
|
1000
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
1001
|
+
# TODO write specific docs for this overload
|
|
1002
|
+
...
|
|
1003
|
+
|
|
541
1004
|
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> MaybeCoro[T]:
|
|
542
|
-
"""
|
|
1005
|
+
"""Calls the wrapped function, defaulting to synchronous execution.
|
|
543
1006
|
|
|
544
1007
|
This method overrides the base :meth:`ASyncFunction.__call__` to provide a synchronous
|
|
545
1008
|
default behavior.
|
|
@@ -548,29 +1011,32 @@ class ASyncFunctionSyncDefault(ASyncFunction[P, T]):
|
|
|
548
1011
|
*args: Positional arguments to pass to the wrapped function.
|
|
549
1012
|
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
550
1013
|
|
|
551
|
-
Returns:
|
|
552
|
-
The result of the wrapped function call.
|
|
553
|
-
|
|
554
1014
|
Raises:
|
|
555
1015
|
Exception: Any exception that may be raised by the wrapped function.
|
|
1016
|
+
|
|
1017
|
+
Returns:
|
|
1018
|
+
The result of the function call.
|
|
1019
|
+
|
|
1020
|
+
See Also:
|
|
1021
|
+
- :meth:`ASyncFunction.__call__`
|
|
556
1022
|
"""
|
|
557
1023
|
return self.fn(*args, **kwargs)
|
|
558
1024
|
|
|
559
1025
|
__docstring_append__ = ":class:`~a_sync.a_sync.function.ASyncFunctionSyncDefault`, you can optionally pass `sync=False` or `asynchronous=True` to force it to return a coroutine. Without either kwarg, it will run synchronously."
|
|
560
|
-
|
|
1026
|
+
|
|
1027
|
+
|
|
561
1028
|
class ASyncFunctionAsyncDefault(ASyncFunction[P, T]):
|
|
562
1029
|
"""
|
|
563
1030
|
A specialized :class:`~ASyncFunction` that defaults to asynchronous execution.
|
|
564
1031
|
|
|
565
1032
|
This class is used when the :func:`~a_sync` decorator is applied with `default='async'`.
|
|
566
|
-
It provides type hints to indicate that the default call behavior is asynchronous
|
|
1033
|
+
It provides type hints to indicate that the default call behavior is asynchronous
|
|
567
1034
|
and supports IDE type checking for most use cases.
|
|
568
1035
|
|
|
569
1036
|
The wrapped function can still be called synchronously by passing `sync=True`
|
|
570
1037
|
or `asynchronous=False` as a keyword argument.
|
|
571
1038
|
|
|
572
1039
|
Example:
|
|
573
|
-
```python
|
|
574
1040
|
@a_sync(default='async')
|
|
575
1041
|
async def my_function(x: int) -> str:
|
|
576
1042
|
return str(x)
|
|
@@ -580,20 +1046,38 @@ class ASyncFunctionAsyncDefault(ASyncFunction[P, T]):
|
|
|
580
1046
|
|
|
581
1047
|
# Synchronous call
|
|
582
1048
|
result = my_function(5, sync=True) # returns "5"
|
|
583
|
-
```
|
|
584
1049
|
"""
|
|
1050
|
+
|
|
585
1051
|
@overload
|
|
586
|
-
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T
|
|
1052
|
+
def __call__(self, *args: P.args, sync: Literal[True], **kwargs: P.kwargs) -> T:
|
|
1053
|
+
# TODO write specific docs for this overload
|
|
1054
|
+
...
|
|
1055
|
+
|
|
587
1056
|
@overload
|
|
588
|
-
def __call__(
|
|
1057
|
+
def __call__(
|
|
1058
|
+
self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs
|
|
1059
|
+
) -> Coroutine[Any, Any, T]:
|
|
1060
|
+
# TODO write specific docs for this overload
|
|
1061
|
+
...
|
|
1062
|
+
|
|
589
1063
|
@overload
|
|
590
|
-
def __call__(
|
|
1064
|
+
def __call__(
|
|
1065
|
+
self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs
|
|
1066
|
+
) -> T:
|
|
1067
|
+
# TODO write specific docs for this overload
|
|
1068
|
+
...
|
|
1069
|
+
|
|
591
1070
|
@overload
|
|
592
|
-
def __call__(
|
|
1071
|
+
def __call__(
|
|
1072
|
+
self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs
|
|
1073
|
+
) -> Coroutine[Any, Any, T]:
|
|
1074
|
+
# TODO write specific docs for this overload
|
|
1075
|
+
...
|
|
1076
|
+
|
|
593
1077
|
@overload
|
|
594
|
-
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, T]
|
|
1078
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, T]: ...
|
|
595
1079
|
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> MaybeCoro[T]:
|
|
596
|
-
"""
|
|
1080
|
+
"""Calls the wrapped function, defaulting to asynchronous execution.
|
|
597
1081
|
|
|
598
1082
|
This method overrides the base :meth:`ASyncFunction.__call__` to provide an asynchronous
|
|
599
1083
|
default behavior.
|
|
@@ -602,39 +1086,117 @@ class ASyncFunctionAsyncDefault(ASyncFunction[P, T]):
|
|
|
602
1086
|
*args: Positional arguments to pass to the wrapped function.
|
|
603
1087
|
**kwargs: Keyword arguments to pass to the wrapped function.
|
|
604
1088
|
|
|
605
|
-
Returns:
|
|
606
|
-
A coroutine object representing the asynchronous execution of the wrapped function.
|
|
607
|
-
|
|
608
1089
|
Raises:
|
|
609
1090
|
Exception: Any exception that may be raised by the wrapped function.
|
|
1091
|
+
|
|
1092
|
+
Returns:
|
|
1093
|
+
The result of the function call.
|
|
1094
|
+
|
|
1095
|
+
See Also:
|
|
1096
|
+
- :meth:`ASyncFunction.__call__`
|
|
610
1097
|
"""
|
|
611
1098
|
return self.fn(*args, **kwargs)
|
|
612
1099
|
|
|
613
1100
|
__docstring_append__ = ":class:`~a_sync.a_sync.function.ASyncFunctionAsyncDefault`, you can optionally pass `sync=True` or `asynchronous=False` to force it to run synchronously and return a value. Without either kwarg, it will return a coroutine for you to await."
|
|
614
1101
|
|
|
1102
|
+
|
|
615
1103
|
class ASyncDecoratorSyncDefault(ASyncDecorator):
|
|
616
1104
|
@overload
|
|
617
1105
|
def __call__(self, func: AnyFn[Concatenate[B, P], T]) -> "ASyncBoundMethodSyncDefault[P, T]": # type: ignore [override]
|
|
618
|
-
|
|
1106
|
+
"""
|
|
1107
|
+
Decorates a bound method with synchronous default behavior.
|
|
1108
|
+
|
|
1109
|
+
Args:
|
|
1110
|
+
func: The bound method to decorate.
|
|
1111
|
+
|
|
1112
|
+
Returns:
|
|
1113
|
+
An ASyncBoundMethodSyncDefault instance with synchronous default behavior.
|
|
1114
|
+
|
|
1115
|
+
See Also:
|
|
1116
|
+
- :class:`ASyncBoundMethodSyncDefault`
|
|
1117
|
+
"""
|
|
1118
|
+
|
|
619
1119
|
@overload
|
|
620
1120
|
def __call__(self, func: AnyBoundMethod[P, T]) -> ASyncFunctionSyncDefault[P, T]: # type: ignore [override]
|
|
621
|
-
|
|
1121
|
+
"""
|
|
1122
|
+
Decorates a bound method with synchronous default behavior.
|
|
1123
|
+
|
|
1124
|
+
Args:
|
|
1125
|
+
func: The bound method to decorate.
|
|
1126
|
+
|
|
1127
|
+
Returns:
|
|
1128
|
+
An ASyncFunctionSyncDefault instance with synchronous default behavior.
|
|
1129
|
+
|
|
1130
|
+
See Also:
|
|
1131
|
+
- :class:`ASyncFunctionSyncDefault`
|
|
1132
|
+
"""
|
|
1133
|
+
|
|
622
1134
|
@overload
|
|
623
1135
|
def __call__(self, func: AnyFn[P, T]) -> ASyncFunctionSyncDefault[P, T]: # type: ignore [override]
|
|
624
|
-
|
|
1136
|
+
"""
|
|
1137
|
+
Decorates a function with synchronous default behavior.
|
|
1138
|
+
|
|
1139
|
+
Args:
|
|
1140
|
+
func: The function to decorate.
|
|
1141
|
+
|
|
1142
|
+
Returns:
|
|
1143
|
+
An ASyncFunctionSyncDefault instance with synchronous default behavior.
|
|
1144
|
+
|
|
1145
|
+
See Also:
|
|
1146
|
+
- :class:`ASyncFunctionSyncDefault`
|
|
1147
|
+
"""
|
|
1148
|
+
|
|
625
1149
|
def __call__(self, func: AnyFn[P, T]) -> ASyncFunctionSyncDefault[P, T]:
|
|
1150
|
+
# TODO write specific docs for this implementation
|
|
626
1151
|
return ASyncFunctionSyncDefault(func, **self.modifiers)
|
|
627
1152
|
|
|
1153
|
+
|
|
628
1154
|
class ASyncDecoratorAsyncDefault(ASyncDecorator):
|
|
629
1155
|
@overload
|
|
630
1156
|
def __call__(self, func: AnyFn[Concatenate[B, P], T]) -> "ASyncBoundMethodAsyncDefault[P, T]": # type: ignore [override]
|
|
631
|
-
|
|
1157
|
+
"""
|
|
1158
|
+
Decorates a bound method with asynchronous default behavior.
|
|
1159
|
+
|
|
1160
|
+
Args:
|
|
1161
|
+
func: The bound method to decorate.
|
|
1162
|
+
|
|
1163
|
+
Returns:
|
|
1164
|
+
An ASyncBoundMethodAsyncDefault instance with asynchronous default behavior.
|
|
1165
|
+
|
|
1166
|
+
See Also:
|
|
1167
|
+
- :class:`ASyncBoundMethodAsyncDefault`
|
|
1168
|
+
"""
|
|
1169
|
+
|
|
632
1170
|
@overload
|
|
633
1171
|
def __call__(self, func: AnyBoundMethod[P, T]) -> ASyncFunctionAsyncDefault[P, T]: # type: ignore [override]
|
|
634
|
-
|
|
1172
|
+
"""
|
|
1173
|
+
Decorates a bound method with asynchronous default behavior.
|
|
1174
|
+
|
|
1175
|
+
Args:
|
|
1176
|
+
func: The bound method to decorate.
|
|
1177
|
+
|
|
1178
|
+
Returns:
|
|
1179
|
+
An ASyncFunctionAsyncDefault instance with asynchronous default behavior.
|
|
1180
|
+
|
|
1181
|
+
See Also:
|
|
1182
|
+
- :class:`ASyncFunctionAsyncDefault`
|
|
1183
|
+
"""
|
|
1184
|
+
|
|
635
1185
|
@overload
|
|
636
1186
|
def __call__(self, func: AnyFn[P, T]) -> ASyncFunctionAsyncDefault[P, T]: # type: ignore [override]
|
|
637
|
-
|
|
1187
|
+
"""
|
|
1188
|
+
Decorates a function with asynchronous default behavior.
|
|
1189
|
+
|
|
1190
|
+
Args:
|
|
1191
|
+
func: The function to decorate.
|
|
1192
|
+
|
|
1193
|
+
Returns:
|
|
1194
|
+
An ASyncFunctionAsyncDefault instance with asynchronous default behavior.
|
|
1195
|
+
|
|
1196
|
+
See Also:
|
|
1197
|
+
- :class:`ASyncFunctionAsyncDefault`
|
|
1198
|
+
"""
|
|
1199
|
+
|
|
638
1200
|
def __call__(self, func: AnyFn[P, T]) -> ASyncFunctionAsyncDefault[P, T]:
|
|
1201
|
+
# TODO write specific docs for this implementation
|
|
639
1202
|
return ASyncFunctionAsyncDefault(func, **self.modifiers)
|
|
640
|
-
|