ez-a-sync 0.22.15__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 (49) hide show
  1. a_sync/ENVIRONMENT_VARIABLES.py +34 -3
  2. a_sync/__init__.py +32 -9
  3. a_sync/_smart.py +105 -6
  4. a_sync/_typing.py +56 -3
  5. a_sync/a_sync/_descriptor.py +174 -12
  6. a_sync/a_sync/_flags.py +64 -3
  7. a_sync/a_sync/_helpers.py +40 -8
  8. a_sync/a_sync/_kwargs.py +30 -6
  9. a_sync/a_sync/_meta.py +35 -6
  10. a_sync/a_sync/abstract.py +57 -9
  11. a_sync/a_sync/config.py +44 -7
  12. a_sync/a_sync/decorator.py +217 -37
  13. a_sync/a_sync/function.py +339 -47
  14. a_sync/a_sync/method.py +241 -52
  15. a_sync/a_sync/modifiers/__init__.py +39 -1
  16. a_sync/a_sync/modifiers/cache/__init__.py +75 -5
  17. a_sync/a_sync/modifiers/cache/memory.py +50 -6
  18. a_sync/a_sync/modifiers/limiter.py +55 -6
  19. a_sync/a_sync/modifiers/manager.py +46 -2
  20. a_sync/a_sync/modifiers/semaphores.py +84 -11
  21. a_sync/a_sync/singleton.py +43 -19
  22. a_sync/asyncio/__init__.py +137 -1
  23. a_sync/asyncio/as_completed.py +44 -38
  24. a_sync/asyncio/create_task.py +46 -10
  25. a_sync/asyncio/gather.py +72 -25
  26. a_sync/exceptions.py +178 -11
  27. a_sync/executor.py +51 -3
  28. a_sync/future.py +671 -29
  29. a_sync/iter.py +64 -7
  30. a_sync/primitives/_debug.py +59 -5
  31. a_sync/primitives/_loggable.py +36 -6
  32. a_sync/primitives/locks/counter.py +74 -7
  33. a_sync/primitives/locks/prio_semaphore.py +87 -8
  34. a_sync/primitives/locks/semaphore.py +68 -20
  35. a_sync/primitives/queue.py +65 -26
  36. a_sync/task.py +51 -15
  37. a_sync/utils/iterators.py +52 -16
  38. {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
  39. ez_a_sync-0.22.16.dist-info/RECORD +74 -0
  40. {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
  41. tests/executor.py +150 -12
  42. tests/test_abstract.py +15 -0
  43. tests/test_base.py +198 -2
  44. tests/test_executor.py +23 -0
  45. tests/test_singleton.py +13 -1
  46. tests/test_task.py +45 -17
  47. ez_a_sync-0.22.15.dist-info/RECORD +0 -74
  48. {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
  49. {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/top_level.txt +0 -0
a_sync/a_sync/method.py CHANGED
@@ -31,8 +31,34 @@ logger = logging.getLogger(__name__)
31
31
 
32
32
  class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
33
33
  """
34
- This class provides the core functionality for creating :class:`ASyncBoundMethod` objects,
35
- which can be used to define methods that can be called both synchronously and asynchronously.
34
+ A descriptor for managing methods that can be called both synchronously and asynchronously.
35
+
36
+ This class provides the core functionality for binding methods to instances and determining
37
+ the execution mode ("sync" or "async") based on various conditions, such as the instance type,
38
+ the method's default setting, or specific flags passed during the method call.
39
+
40
+ The descriptor is responsible for creating an appropriate bound method when accessed,
41
+ which is the actual object that can be called in both synchronous and asynchronous contexts.
42
+ It can create different types of bound methods (`ASyncBoundMethodSyncDefault`,
43
+ `ASyncBoundMethodAsyncDefault`, or `ASyncBoundMethod`) based on the default mode or instance type.
44
+
45
+ It also manages cache handles for bound methods and prevents setting or deleting the descriptor.
46
+
47
+ Examples:
48
+ >>> class MyClass:
49
+ ... @ASyncMethodDescriptor
50
+ ... async def my_method(self):
51
+ ... return "Hello, World!"
52
+ ...
53
+ >>> obj = MyClass()
54
+ >>> await obj.my_method()
55
+ 'Hello, World!'
56
+ >>> obj.my_method(sync=True)
57
+ 'Hello, World!'
58
+
59
+ See Also:
60
+ - :class:`ASyncBoundMethod`
61
+ - :class:`ASyncFunction`
36
62
  """
37
63
 
38
64
  __wrapped__: AnyFn[P, T]
@@ -47,8 +73,9 @@ class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
47
73
  *args: Positional arguments.
48
74
  **kwargs: Keyword arguments.
49
75
 
50
- Returns:
51
- The result of the method call.
76
+ Examples:
77
+ >>> descriptor = ASyncMethodDescriptor(my_async_function)
78
+ >>> await descriptor(instance, arg1, arg2, kwarg1=value1)
52
79
  """
53
80
  # NOTE: This is only used by TaskMapping atm # TODO: use it elsewhere
54
81
  logger.debug(
@@ -74,8 +101,9 @@ class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
74
101
  instance: The instance to bind the method to, or None.
75
102
  owner: The owner class.
76
103
 
77
- Returns:
78
- The descriptor or bound method.
104
+ Examples:
105
+ >>> descriptor = ASyncMethodDescriptor(my_function)
106
+ >>> bound_method = descriptor.__get__(instance, MyClass)
79
107
  """
80
108
  if instance is None:
81
109
  return self
@@ -136,7 +164,12 @@ class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
136
164
  value: The value to set.
137
165
 
138
166
  Raises:
139
- :class:`RuntimeError`: Always raised to prevent setting.
167
+ RuntimeError: Always raised to prevent setting.
168
+
169
+ Examples:
170
+ >>> descriptor = ASyncMethodDescriptor(my_function)
171
+ >>> descriptor.__set__(instance, value)
172
+ RuntimeError: cannot set field_name, descriptor is what you get. sorry.
140
173
  """
141
174
  raise RuntimeError(
142
175
  f"cannot set {self.field_name}, {self} is what you get. sorry."
@@ -150,7 +183,12 @@ class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
150
183
  instance: The instance.
151
184
 
152
185
  Raises:
153
- :class:`RuntimeError`: Always raised to prevent deletion.
186
+ RuntimeError: Always raised to prevent deletion.
187
+
188
+ Examples:
189
+ >>> descriptor = ASyncMethodDescriptor(my_function)
190
+ >>> descriptor.__delete__(instance)
191
+ RuntimeError: cannot delete field_name, you're stuck with descriptor forever. sorry.
154
192
  """
155
193
  raise RuntimeError(
156
194
  f"cannot delete {self.field_name}, you're stuck with {self} forever. sorry."
@@ -161,8 +199,10 @@ class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
161
199
  """
162
200
  Check if the wrapped function is a coroutine function.
163
201
 
164
- Returns:
165
- True if the wrapped function is a coroutine function, False otherwise.
202
+ Examples:
203
+ >>> descriptor = ASyncMethodDescriptor(my_function)
204
+ >>> descriptor.__is_async_def__
205
+ True
166
206
  """
167
207
  return asyncio.iscoroutinefunction(self.__wrapped__)
168
208
 
@@ -175,6 +215,10 @@ class ASyncMethodDescriptor(ASyncDescriptor[I, P, T]):
175
215
 
176
216
  Returns:
177
217
  A timer handle for cache management.
218
+
219
+ Examples:
220
+ >>> descriptor = ASyncMethodDescriptor(my_function)
221
+ >>> cache_handle = descriptor._get_cache_handle(instance)
178
222
  """
179
223
  # NOTE: use `instance.__dict__.pop` instead of `delattr` so we don't create a strong ref to `instance`
180
224
  return asyncio.get_event_loop().call_later(
@@ -187,8 +231,28 @@ class ASyncMethodDescriptorSyncDefault(ASyncMethodDescriptor[I, P, T]):
187
231
  """
188
232
  A descriptor for :class:`ASyncBoundMethodSyncDefault` objects.
189
233
 
190
- This class extends ASyncMethodDescriptor to provide a synchronous
191
- default behavior for method calls.
234
+ This class extends :class:`ASyncMethodDescriptor` to provide a synchronous
235
+ default behavior for method calls. It specifically creates `ASyncBoundMethodSyncDefault`
236
+ instances, which are specialized versions of `ASyncBoundMethod` with synchronous default behavior.
237
+
238
+ Examples:
239
+ >>> class MyClass:
240
+ ... @ASyncMethodDescriptorSyncDefault
241
+ ... def my_method(self):
242
+ ... return "Hello, World!"
243
+ ...
244
+ >>> obj = MyClass()
245
+ >>> obj.my_method()
246
+ 'Hello, World!'
247
+ >>> coro = obj.my_method(sync=False)
248
+ >>> coro
249
+ <coroutine object MyClass.my_method at 0x7fb4f5fb49c0>
250
+ >>> await coro
251
+ 'Hello, World!'
252
+
253
+ See Also:
254
+ - :class:`ASyncBoundMethodSyncDefault`
255
+ - :class:`ASyncFunctionSyncDefault`
192
256
  """
193
257
 
194
258
  default = "sync"
@@ -229,8 +293,9 @@ class ASyncMethodDescriptorSyncDefault(ASyncMethodDescriptor[I, P, T]):
229
293
  instance: The instance to bind the method to, or None.
230
294
  owner: The owner class.
231
295
 
232
- Returns:
233
- The descriptor or bound method with synchronous default.
296
+ Examples:
297
+ >>> descriptor = ASyncMethodDescriptorSyncDefault(my_function)
298
+ >>> bound_method = descriptor.__get__(instance, MyClass)
234
299
  """
235
300
  if instance is None:
236
301
  return self
@@ -254,8 +319,27 @@ class ASyncMethodDescriptorAsyncDefault(ASyncMethodDescriptor[I, P, T]):
254
319
  """
255
320
  A descriptor for asynchronous methods with an asynchronous default.
256
321
 
257
- This class extends ASyncMethodDescriptor to provide an asynchronous default
258
- behavior for method calls.
322
+ This class extends :class:`ASyncMethodDescriptor` to provide an asynchronous default
323
+ behavior for method calls. It specifically creates `ASyncBoundMethodAsyncDefault`
324
+ instances, which are specialized versions of `ASyncBoundMethod` with asynchronous default behavior.
325
+
326
+ Examples:
327
+ >>> class MyClass:
328
+ ... @ASyncMethodDescriptorAsyncDefault
329
+ ... async def my_method(self):
330
+ ... return "Hello, World!"
331
+ ...
332
+ >>> obj = MyClass()
333
+ >>> coro = obj.my_method()
334
+ >>> coro
335
+ <coroutine object MyClass.my_method at 0x7fb4f5fb49c0>
336
+ >>> await coro
337
+ >>> obj.my_method(sync=True)
338
+ 'Hello, World!'
339
+
340
+ See Also:
341
+ - :class:`ASyncBoundMethodAsyncDefault`
342
+ - :class:`ASyncFunctionAsyncDefault`
259
343
  """
260
344
 
261
345
  default = "async"
@@ -294,8 +378,9 @@ class ASyncMethodDescriptorAsyncDefault(ASyncMethodDescriptor[I, P, T]):
294
378
  instance: The instance to bind the method to, or None.
295
379
  owner: The owner class.
296
380
 
297
- Returns:
298
- The descriptor or bound method with asynchronous default.
381
+ Examples:
382
+ >>> descriptor = ASyncMethodDescriptorAsyncDefault(my_function)
383
+ >>> bound_method = descriptor.__get__(instance, MyClass)
299
384
  """
300
385
  if instance is None:
301
386
  return self
@@ -319,16 +404,37 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
319
404
  A bound method that can be called both synchronously and asynchronously.
320
405
 
321
406
  This class represents a method bound to an instance, which can be called
322
- either synchronously or asynchronously based on various conditions.
407
+ either synchronously or asynchronously based on various conditions. It handles
408
+ caching of bound methods and includes logic for determining whether to await
409
+ the method call based on flags or default settings.
410
+
411
+ Examples:
412
+ >>> class MyClass:
413
+ ... def __init__(self, value):
414
+ ... self.value = value
415
+ ...
416
+ ... @ASyncMethodDescriptor
417
+ ... async def my_method(self):
418
+ ... return self.value
419
+ ...
420
+ >>> obj = MyClass(42)
421
+ >>> await obj.my_method()
422
+ 42
423
+ >>> obj.my_method(sync=True)
424
+ 42
425
+
426
+ See Also:
427
+ - :class:`ASyncMethodDescriptor`
428
+ - :class:`ASyncFunction`
323
429
  """
324
430
 
325
431
  # NOTE: this is created by the Descriptor
326
432
 
327
433
  _cache_handle: asyncio.TimerHandle
328
- "An asyncio handle used to pop the bound method from `instance.__dict__` 5 minutes after its last use."
434
+ """An asyncio handle used to pop the bound method from `instance.__dict__` 5 minutes after its last use."""
329
435
 
330
436
  __weakself__: "weakref.ref[I]"
331
- "A weak reference to the instance the function is bound to."
437
+ """A weak reference to the instance the function is bound to."""
332
438
 
333
439
  __wrapped__: AnyFn[Concatenate[I, P], T]
334
440
  """The original unbound method that was wrapped."""
@@ -350,6 +456,18 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
350
456
  unbound: The unbound function.
351
457
  async_def: Whether the original function is an async def.
352
458
  **modifiers: Additional modifiers for the function.
459
+
460
+ Examples:
461
+ >>> class MyClass:
462
+ ... def __init__(self, value):
463
+ ... self.value = value
464
+ ...
465
+ ... @ASyncMethodDescriptor
466
+ ... async def my_method(self):
467
+ ... return self.value
468
+ ...
469
+ >>> obj = MyClass(42)
470
+ >>> bound_method = ASyncBoundMethod(obj, MyClass.my_method, True)
353
471
  """
354
472
  self.__weakself__ = weakref.ref(instance, self.__cancel_cache_handle)
355
473
  # First we unwrap the coro_fn and rewrap it so overriding flag kwargs are handled automagically.
@@ -358,15 +476,17 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
358
476
  unbound = unbound.__wrapped__
359
477
  ASyncFunction.__init__(self, unbound, **modifiers)
360
478
  self._is_async_def = async_def
361
- "True if `self.__wrapped__` is a coroutine function, False otherwise."
479
+ """True if `self.__wrapped__` is a coroutine function, False otherwise."""
362
480
  functools.update_wrapper(self, unbound)
363
481
 
364
482
  def __repr__(self) -> str:
365
483
  """
366
484
  Return a string representation of the bound method.
367
485
 
368
- Returns:
369
- A string representation of the bound method.
486
+ Examples:
487
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
488
+ >>> repr(bound_method)
489
+ '<ASyncBoundMethod for function module.ClassName.method_name bound to instance>'
370
490
  """
371
491
  try:
372
492
  instance_type = type(self.__self__)
@@ -401,8 +521,10 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
401
521
  *args: Positional arguments.
402
522
  **kwargs: Keyword arguments.
403
523
 
404
- Returns:
405
- The result of the method call, which may be a coroutine.
524
+ Examples:
525
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
526
+ >>> await bound_method(arg1, arg2, kwarg1=value1)
527
+ >>> bound_method(arg1, arg2, kwarg1=value1, sync=True)
406
528
  """
407
529
  logger.debug("calling %s with args: %s kwargs: %s", self, args, kwargs)
408
530
  # This could either be a coroutine or a return value from an awaited coroutine,
@@ -428,11 +550,13 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
428
550
  """
429
551
  Get the instance the method is bound to.
430
552
 
431
- Returns:
432
- The instance the method is bound to.
433
-
434
553
  Raises:
435
- :class:`ReferenceError`: If the instance has been garbage collected.
554
+ ReferenceError: If the instance has been garbage collected.
555
+
556
+ Examples:
557
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
558
+ >>> bound_method.__self__
559
+ <MyClass instance>
436
560
  """
437
561
  instance = self.__weakself__()
438
562
  if instance is not None:
@@ -444,8 +568,10 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
444
568
  """
445
569
  Check if the method is bound to an ASyncABC instance.
446
570
 
447
- Returns:
448
- True if bound to an ASyncABC instance, False otherwise.
571
+ Examples:
572
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
573
+ >>> bound_method.__bound_to_a_sync_instance__
574
+ True
449
575
  """
450
576
  from a_sync.a_sync.abstract import ASyncABC
451
577
 
@@ -469,6 +595,11 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
469
595
 
470
596
  Returns:
471
597
  A TaskMapping instance for this method.
598
+
599
+ Examples:
600
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
601
+ >>> task_mapping = bound_method.map(iterable1, iterable2, concurrency=5)
602
+ TODO briefly include how someone would then use task_mapping
472
603
  """
473
604
  from a_sync import TaskMapping
474
605
 
@@ -492,8 +623,9 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
492
623
  task_name: Optional name for the task.
493
624
  **kwargs: Additional keyword arguments.
494
625
 
495
- Returns:
496
- True if any result is truthy, False otherwise.
626
+ Examples:
627
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
628
+ >>> result = await bound_method.any(iterable1, iterable2)
497
629
  """
498
630
  return await self.map(
499
631
  *iterables, concurrency=concurrency, task_name=task_name, **kwargs
@@ -515,8 +647,9 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
515
647
  task_name: Optional name for the task.
516
648
  **kwargs: Additional keyword arguments.
517
649
 
518
- Returns:
519
- True if all results are truthy, False otherwise.
650
+ Examples:
651
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
652
+ >>> result = await bound_method.all(iterable1, iterable2)
520
653
  """
521
654
  return await self.map(
522
655
  *iterables, concurrency=concurrency, task_name=task_name, **kwargs
@@ -538,8 +671,9 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
538
671
  task_name: Optional name for the task.
539
672
  **kwargs: Additional keyword arguments.
540
673
 
541
- Returns:
542
- The minimum result.
674
+ Examples:
675
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
676
+ >>> result = await bound_method.min(iterable1, iterable2)
543
677
  """
544
678
  return await self.map(
545
679
  *iterables, concurrency=concurrency, task_name=task_name, **kwargs
@@ -561,8 +695,9 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
561
695
  task_name: Optional name for the task.
562
696
  **kwargs: Additional keyword arguments.
563
697
 
564
- Returns:
565
- The maximum result.
698
+ Examples:
699
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
700
+ >>> result = await bound_method.max(iterable1, iterable2)
566
701
  """
567
702
  return await self.map(
568
703
  *iterables, concurrency=concurrency, task_name=task_name, **kwargs
@@ -584,8 +719,9 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
584
719
  task_name: Optional name for the task.
585
720
  **kwargs: Additional keyword arguments.
586
721
 
587
- Returns:
588
- The sum of the results.
722
+ Examples:
723
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
724
+ >>> result = await bound_method.sum(iterable1, iterable2)
589
725
  """
590
726
  return await self.map(
591
727
  *iterables, concurrency=concurrency, task_name=task_name, **kwargs
@@ -598,8 +734,9 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
598
734
  Args:
599
735
  kwargs: Keyword arguments passed to the method.
600
736
 
601
- Returns:
602
- True if the method should be awaited, False otherwise.
737
+ Examples:
738
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
739
+ >>> should_await = bound_method._should_await(kwargs)
603
740
  """
604
741
  if flag := _kwargs.get_flag_name(kwargs):
605
742
  return _kwargs.is_sync(flag, kwargs, pop_flag=True) # type: ignore [arg-type]
@@ -616,6 +753,10 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
616
753
 
617
754
  Args:
618
755
  instance: The instance associated with the cache handle.
756
+
757
+ Examples:
758
+ >>> bound_method = ASyncBoundMethod(instance, my_function, True)
759
+ >>> bound_method.__cancel_cache_handle(instance)
619
760
  """
620
761
  cache_handle: asyncio.TimerHandle = self._cache_handle
621
762
  cache_handle.cancel()
@@ -624,6 +765,28 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]):
624
765
  class ASyncBoundMethodSyncDefault(ASyncBoundMethod[I, P, T]):
625
766
  """
626
767
  A bound method with synchronous default behavior.
768
+
769
+ This class is a specialized version of :class:`ASyncBoundMethod` that defaults to synchronous execution.
770
+ It overrides the `__call__` method to enforce synchronous default behavior.
771
+
772
+ Examples:
773
+ >>> class MyClass:
774
+ ... def __init__(self, value):
775
+ ... self.value = value
776
+ ...
777
+ ... @ASyncMethodDescriptorSyncDefault
778
+ ... def my_method(self):
779
+ ... return self.value
780
+ ...
781
+ >>> obj = MyClass(42)
782
+ >>> obj.my_method()
783
+ 42
784
+ >>> await obj.my_method(sync=False)
785
+ 42
786
+
787
+ See Also:
788
+ - :class:`ASyncBoundMethod`
789
+ - :class:`ASyncMethodDescriptorSyncDefault`
627
790
  """
628
791
 
629
792
  def __get__(
@@ -636,8 +799,9 @@ class ASyncBoundMethodSyncDefault(ASyncBoundMethod[I, P, T]):
636
799
  instance: The instance to bind the method to, or None.
637
800
  owner: The owner class.
638
801
 
639
- Returns:
640
- The bound method with synchronous default behavior.
802
+ Examples:
803
+ >>> descriptor = ASyncMethodDescriptorSyncDefault(my_function)
804
+ >>> bound_method = descriptor.__get__(instance, MyClass)
641
805
  """
642
806
  return ASyncBoundMethod.__get__(self, instance, owner)
643
807
 
@@ -665,8 +829,9 @@ class ASyncBoundMethodSyncDefault(ASyncBoundMethod[I, P, T]):
665
829
  *args: Positional arguments.
666
830
  **kwargs: Keyword arguments.
667
831
 
668
- Returns:
669
- The result of the method call.
832
+ Examples:
833
+ >>> bound_method = ASyncBoundMethodSyncDefault(instance, my_function, True)
834
+ >>> bound_method(arg1, arg2, kwarg1=value1)
670
835
  """
671
836
  return ASyncBoundMethod.__call__(self, *args, **kwargs)
672
837
 
@@ -674,6 +839,28 @@ class ASyncBoundMethodSyncDefault(ASyncBoundMethod[I, P, T]):
674
839
  class ASyncBoundMethodAsyncDefault(ASyncBoundMethod[I, P, T]):
675
840
  """
676
841
  A bound method with asynchronous default behavior.
842
+
843
+ This class is a specialized version of :class:`ASyncBoundMethod` that defaults to asynchronous execution.
844
+ It overrides the `__call__` method to enforce asynchronous default behavior.
845
+
846
+ Examples:
847
+ >>> class MyClass:
848
+ ... def __init__(self, value):
849
+ ... self.value = value
850
+ ...
851
+ ... @ASyncMethodDescriptorAsyncDefault
852
+ ... async def my_method(self):
853
+ ... return self.value
854
+ ...
855
+ >>> obj = MyClass(42)
856
+ >>> await obj.my_method()
857
+ 42
858
+ >>> obj.my_method(sync=True)
859
+ 42
860
+
861
+ See Also:
862
+ - :class:`ASyncBoundMethod`
863
+ - :class:`ASyncMethodDescriptorAsyncDefault`
677
864
  """
678
865
 
679
866
  def __get__(self, instance: I, owner: Type[I]) -> ASyncFunctionAsyncDefault[P, T]:
@@ -684,8 +871,9 @@ class ASyncBoundMethodAsyncDefault(ASyncBoundMethod[I, P, T]):
684
871
  instance: The instance to bind the method to.
685
872
  owner: The owner class.
686
873
 
687
- Returns:
688
- The bound method with asynchronous default behavior.
874
+ Examples:
875
+ >>> descriptor = ASyncMethodDescriptorAsyncDefault(my_function)
876
+ >>> bound_method = descriptor.__get__(instance, MyClass)
689
877
  """
690
878
  return ASyncBoundMethod.__get__(self, instance, owner)
691
879
 
@@ -713,7 +901,8 @@ class ASyncBoundMethodAsyncDefault(ASyncBoundMethod[I, P, T]):
713
901
  *args: Positional arguments.
714
902
  **kwargs: Keyword arguments.
715
903
 
716
- Returns:
717
- A coroutine representing the asynchronous method call.
904
+ Examples:
905
+ >>> bound_method = ASyncBoundMethodAsyncDefault(instance, my_function, True)
906
+ >>> await bound_method(arg1, arg2, kwarg1=value1)
718
907
  """
719
908
  return ASyncBoundMethod.__call__(self, *args, **kwargs)
@@ -16,8 +16,13 @@ The modifiers available are:
16
16
  - `ram_cache_ttl`: Defines the time-to-live for items in the cache.
17
17
  - `runs_per_minute`: Sets a rate limit for function execution.
18
18
  - `semaphore`: Specifies a semaphore for controlling concurrency.
19
- - `executor`: Defines the executor for synchronous functions.
19
+ - `executor`: Defines the executor for synchronous functions. This is not applied like the other modifiers but is used to manage the execution context for synchronous functions when they are called in an asynchronous manner.
20
20
 
21
+ See Also:
22
+ - :mod:`a_sync.a_sync.modifiers.cache`
23
+ - :mod:`a_sync.a_sync.modifiers.limiter`
24
+ - :mod:`a_sync.a_sync.modifiers.manager`
25
+ - :mod:`a_sync.a_sync.modifiers.semaphores`
21
26
  """
22
27
 
23
28
  from aiolimiter import AsyncLimiter
@@ -36,6 +41,24 @@ def get_modifiers_from(thing: Union[dict, type, object]) -> ModifierKwargs:
36
41
 
37
42
  Returns:
38
43
  A ModifierKwargs object containing the valid modifiers extracted from the input.
44
+
45
+ Examples:
46
+ Extracting modifiers from a class:
47
+
48
+ >>> class Example:
49
+ ... cache_type = 'memory'
50
+ ... runs_per_minute = 60
51
+ >>> get_modifiers_from(Example)
52
+ ModifierKwargs({'cache_type': 'memory', 'runs_per_minute': 60})
53
+
54
+ Extracting modifiers from a dictionary:
55
+
56
+ >>> modifiers_dict = {'cache_type': 'memory', 'semaphore': 5}
57
+ >>> get_modifiers_from(modifiers_dict)
58
+ ModifierKwargs({'cache_type': 'memory', 'semaphore': ThreadsafeSemaphore(5)})
59
+
60
+ See Also:
61
+ - :class:`a_sync.a_sync.modifiers.manager.ModifierManager`
39
62
  """
40
63
  if isinstance(thing, dict):
41
64
  apply_class_defined_modifiers(thing)
@@ -54,6 +77,21 @@ def apply_class_defined_modifiers(attrs_from_metaclass: dict):
54
77
 
55
78
  Args:
56
79
  attrs_from_metaclass: A dictionary of attributes from a metaclass.
80
+
81
+ Examples:
82
+ Applying modifiers to a dictionary:
83
+
84
+ >>> attrs = {'semaphore': 3, 'runs_per_minute': 60}
85
+ >>> apply_class_defined_modifiers(attrs)
86
+ >>> attrs['semaphore']
87
+ ThreadsafeSemaphore(3)
88
+
89
+ >>> attrs['runs_per_minute']
90
+ AsyncLimiter(60)
91
+
92
+ See Also:
93
+ - :class:`a_sync.primitives.locks.ThreadsafeSemaphore`
94
+ - :class:`aiolimiter.AsyncLimiter`
57
95
  """
58
96
  if isinstance(val := attrs_from_metaclass.get("semaphore"), int):
59
97
  attrs_from_metaclass["semaphore"] = ThreadsafeSemaphore(val)