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.

Files changed (73) hide show
  1. a_sync/ENVIRONMENT_VARIABLES.py +37 -5
  2. a_sync/__init__.py +53 -12
  3. a_sync/_smart.py +231 -28
  4. a_sync/_typing.py +112 -15
  5. a_sync/a_sync/__init__.py +35 -10
  6. a_sync/a_sync/_descriptor.py +248 -38
  7. a_sync/a_sync/_flags.py +78 -9
  8. a_sync/a_sync/_helpers.py +46 -13
  9. a_sync/a_sync/_kwargs.py +33 -8
  10. a_sync/a_sync/_meta.py +149 -28
  11. a_sync/a_sync/abstract.py +150 -28
  12. a_sync/a_sync/base.py +34 -16
  13. a_sync/a_sync/config.py +85 -14
  14. a_sync/a_sync/decorator.py +441 -139
  15. a_sync/a_sync/function.py +709 -147
  16. a_sync/a_sync/method.py +437 -110
  17. a_sync/a_sync/modifiers/__init__.py +85 -5
  18. a_sync/a_sync/modifiers/cache/__init__.py +116 -17
  19. a_sync/a_sync/modifiers/cache/memory.py +130 -20
  20. a_sync/a_sync/modifiers/limiter.py +101 -22
  21. a_sync/a_sync/modifiers/manager.py +142 -16
  22. a_sync/a_sync/modifiers/semaphores.py +121 -15
  23. a_sync/a_sync/property.py +383 -82
  24. a_sync/a_sync/singleton.py +44 -19
  25. a_sync/aliases.py +0 -1
  26. a_sync/asyncio/__init__.py +140 -1
  27. a_sync/asyncio/as_completed.py +213 -79
  28. a_sync/asyncio/create_task.py +70 -20
  29. a_sync/asyncio/gather.py +125 -58
  30. a_sync/asyncio/utils.py +3 -3
  31. a_sync/exceptions.py +248 -26
  32. a_sync/executor.py +164 -69
  33. a_sync/future.py +1227 -168
  34. a_sync/iter.py +173 -56
  35. a_sync/primitives/__init__.py +14 -2
  36. a_sync/primitives/_debug.py +72 -18
  37. a_sync/primitives/_loggable.py +41 -10
  38. a_sync/primitives/locks/__init__.py +5 -2
  39. a_sync/primitives/locks/counter.py +107 -38
  40. a_sync/primitives/locks/event.py +21 -7
  41. a_sync/primitives/locks/prio_semaphore.py +262 -63
  42. a_sync/primitives/locks/semaphore.py +138 -89
  43. a_sync/primitives/queue.py +601 -60
  44. a_sync/sphinx/__init__.py +0 -1
  45. a_sync/sphinx/ext.py +160 -50
  46. a_sync/task.py +313 -112
  47. a_sync/utils/__init__.py +12 -6
  48. a_sync/utils/iterators.py +170 -50
  49. {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
  50. ez_a_sync-0.22.16.dist-info/RECORD +74 -0
  51. {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
  52. tests/conftest.py +1 -2
  53. tests/executor.py +250 -9
  54. tests/fixtures.py +61 -32
  55. tests/test_abstract.py +22 -4
  56. tests/test_as_completed.py +54 -21
  57. tests/test_base.py +264 -19
  58. tests/test_cache.py +31 -15
  59. tests/test_decorator.py +54 -28
  60. tests/test_executor.py +31 -13
  61. tests/test_future.py +45 -8
  62. tests/test_gather.py +8 -2
  63. tests/test_helpers.py +2 -0
  64. tests/test_iter.py +55 -13
  65. tests/test_limiter.py +5 -3
  66. tests/test_meta.py +23 -9
  67. tests/test_modified.py +4 -1
  68. tests/test_semaphore.py +15 -8
  69. tests/test_singleton.py +28 -11
  70. tests/test_task.py +162 -36
  71. ez_a_sync-0.22.14.dist-info/RECORD +0 -74
  72. {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
  73. {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
- AsyncPropertyDescriptor # type: ignore [import]
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 (ASyncBoundMethod, ASyncBoundMethodAsyncDefault,
20
- ASyncBoundMethodSyncDefault)
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
- class ModifiedMixin:
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 to handle
29
- the application of async and sync modifiers to functions.
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
- Convert a synchronous function to an asynchronous one and apply async modifiers.
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
- An asynchronous function with async modifiers applied.
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
- Apply sync modifiers to the _helpers._await function and cache it.
64
+ Applies sync modifiers to the _helpers._await function and caches it.
52
65
 
53
66
  Returns:
54
- A function that applies sync modifiers to awaitable objects.
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
- Get the default execution mode (sync, async, or None) for the function.
78
+ Gets the default execution mode (sync, async, or None) for the function.
62
79
 
63
80
  Returns:
64
- The default execution mode as determined by the modifiers.
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 # These are always valid
103
+ return # These are always valid
73
104
  if not callable(fn):
74
- raise TypeError(f'Input is not callable. Unable to decorate {fn}')
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(f"{fn} must not have any arguments with the following names: {_flags.VIABLE_FLAGS}")
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(ModifiedMixin, Generic[P, T]):
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
- Initialize an ASyncFunction instance.
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__ += f"\n\nSince `{self.__name__}` is an {self.__docstring_append__}"
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__(self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs) -> Coroutine[Any, Any, T]:...
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__(self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs) -> T:...
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__(self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs) -> Coroutine[Any, Any, T]:...
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
- Call the wrapped function either synchronously or asynchronously.
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("calling %s fn: %s with args: %s kwargs: %s", self, self.fn, args, kwargs)
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): # -> Union[SyncFn[[CoroFn[P, T]], MaybeAwaitable[T]], SyncFn[[SyncFn[P, T]], MaybeAwaitable[T]]]:
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(self, *iterables: AnyIterable[P.args], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> "TaskMapping[P, T]":
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
- Create a TaskMapping for the wrapped function with the given iterables.
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
- async def any(self, *iterables: AnyIterable[P.args], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> bool:
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
- Check if any result of the function applied to the iterables is truthy.
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
- A boolean indicating if any result is truthy.
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
- async def all(self, *iterables: AnyIterable[P.args], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> bool:
387
+ See Also:
388
+ - :meth:`map`
214
389
  """
215
- Check if all results of the function applied to the iterables are truthy.
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
- A boolean indicating if all results are truthy.
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
- async def min(self, *iterables: AnyIterable[P.args], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> T:
416
+ See Also:
417
+ - :meth:`map`
229
418
  """
230
- Find the minimum result of the function applied to the iterables.
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
- async def max(self, *iterables: AnyIterable[P.args], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> T:
445
+ See Also:
446
+ - :meth:`map`
244
447
  """
245
- Find the maximum result of the function applied to the iterables.
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
- async def sum(self, *iterables: AnyIterable[P.args], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> T:
474
+ See Also:
475
+ - :meth:`map`
259
476
  """
260
- Calculate the sum of the results of the function applied to the iterables.
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(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).sum(pop=True, sync=False)
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
- def map(self, *iterables: AnyIterable[Any], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> "TaskMapping[P, T]":
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
- Create a TaskMapping for the wrapped function with the given iterables.
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
- async def any(self, *iterables: AnyIterable[Any], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> bool:
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
- Check if any result of the function applied to the iterables is truthy.
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
- A boolean indicating if any result is truthy.
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
- async def all(self, *iterables: AnyIterable[Any], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> bool:
566
+ See Also:
567
+ - :meth:`map`
305
568
  """
306
- Check if all results of the function applied to the iterables are truthy.
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
- A boolean indicating if all results are truthy.
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
- async def min(self, *iterables: AnyIterable[Any], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> T:
595
+ See Also:
596
+ - :meth:`map`
320
597
  """
321
- Find the minimum result of the function applied to the iterables.
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
- async def max(self, *iterables: AnyIterable[Any], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> T:
624
+ See Also:
625
+ - :meth:`map`
335
626
  """
336
- Find the maximum result of the function applied to the iterables.
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
- async def sum(self, *iterables: AnyIterable[Any], concurrency: Optional[int] = None, task_name: str = "", **function_kwargs: P.kwargs) -> T:
653
+ See Also:
654
+ - :meth:`map`
350
655
  """
351
- Calculate the sum of the results of the function applied to the iterables.
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(*iterables, concurrency=concurrency, task_name=task_name, **function_kwargs).sum(pop=True, sync=False)
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
- Determine the default execution mode (sync or async) for the function.
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 the default is async.
701
+ True if the default is sync, False if async.
702
+
703
+ See Also:
704
+ - :attr:`default`
374
705
  """
375
- return True if self.default == 'sync' else False if self.default == 'async' else not self._async_def
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
- Check if the wrapped function is an asynchronous function.
715
+ Checks if the wrapped function is an asynchronous function.
381
716
 
382
717
  Returns:
383
- True if the wrapped function is an asynchronous function, False otherwise.
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
- Determine whether to run the function synchronously or asynchronously.
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 be run synchronously, False otherwise.
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
- Convert the wrapped function to an asynchronous function and apply both sync and async modifiers.
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
- An asynchronous function with both sync and async modifiers applied.
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(f"Can only be applied to sync functions, not {self.__wrapped__}")
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
- Apply modifiers to the wrapped function.
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 wrapped function with modifiers applied.
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): # -> SyncFn[[CoroFn[P, T]], MaybeAwaitable[T]]:
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 final wrapped function.
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(kwargs) # Must take place before coro is created, we're popping a kwarg.
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): # -> SyncFn[[SyncFn[P, T]], MaybeAwaitable[T]]:
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 final wrapped function.
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
- class ASyncDecorator(ModifiedMixin):
843
+
844
+
845
+ class ASyncDecorator(_ModifiedMixin):
476
846
  def __init__(self, **modifiers: Unpack[ModifierKwargs]) -> None:
477
- assert 'default' in modifiers, modifiers
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
- if self.modifiers.default not in ['sync', 'async', None]:
483
- raise ValueError(f"'default' must be either 'sync', 'async', or None. You passed {self.modifiers.default}.")
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__(self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs) -> Coroutine[Any, Any, T]:...
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__(self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs) -> T:...
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__(self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs) -> Coroutine[Any, Any, T]:...
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
- """Call the wrapped function, defaulting to synchronous execution.
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__(self, *args: P.args, sync: Literal[False], **kwargs: P.kwargs) -> Coroutine[Any, Any, T]:...
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__(self, *args: P.args, asynchronous: Literal[False], **kwargs: P.kwargs) -> T:...
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__(self, *args: P.args, asynchronous: Literal[True], **kwargs: P.kwargs) -> Coroutine[Any, Any, T]:...
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
- """Call the wrapped function, defaulting to asynchronous execution.
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
-