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
tests/executor.py CHANGED
@@ -8,7 +8,16 @@ from a_sync import ProcessPoolExecutor, ThreadPoolExecutor, PruningThreadPoolExe
8
8
 
9
9
  @pytest.mark.asyncio
10
10
  async def test_process_pool_executor_run():
11
- """Tests the ProcessPoolExecutor by running and submitting the work function asynchronously."""
11
+ """Tests the :class:`ProcessPoolExecutor` by running and submitting the work function asynchronously.
12
+
13
+ This test ensures that the `run` method of the `ProcessPoolExecutor` returns a coroutine
14
+ when executed with a synchronous function.
15
+
16
+ See Also:
17
+ - :meth:`ProcessPoolExecutor.run`
18
+ - :func:`asyncio.iscoroutine`
19
+
20
+ """
12
21
  executor = ProcessPoolExecutor(1)
13
22
  coro = executor.run(time.sleep, 0.1)
14
23
  assert asyncio.iscoroutine(coro)
@@ -17,7 +26,16 @@ async def test_process_pool_executor_run():
17
26
 
18
27
  @pytest.mark.asyncio
19
28
  async def test_thread_pool_executor_run():
20
- """Tests the ThreadPoolExecutor by running and submitting the work function asynchronously."""
29
+ """Tests the :class:`ThreadPoolExecutor` by running and submitting the work function asynchronously.
30
+
31
+ This test ensures that the `run` method of the `ThreadPoolExecutor` returns a coroutine
32
+ when executed with a synchronous function.
33
+
34
+ See Also:
35
+ - :meth:`ThreadPoolExecutor.run`
36
+ - :func:`asyncio.iscoroutine`
37
+
38
+ """
21
39
  executor = ThreadPoolExecutor(1)
22
40
  coro = executor.run(time.sleep, 0.1)
23
41
  assert asyncio.iscoroutine(coro)
@@ -26,7 +44,16 @@ async def test_thread_pool_executor_run():
26
44
 
27
45
  @pytest.mark.asyncio
28
46
  async def test_pruning_thread_pool_executor_run():
29
- """Tests the PruningThreadPoolExecutor by running and submitting the work function asynchronously."""
47
+ """Tests the :class:`PruningThreadPoolExecutor` by running and submitting the work function asynchronously.
48
+
49
+ This test ensures that the `run` method of the `PruningThreadPoolExecutor` returns a coroutine
50
+ when executed with a synchronous function.
51
+
52
+ See Also:
53
+ - :meth:`PruningThreadPoolExecutor.run`
54
+ - :func:`asyncio.iscoroutine`
55
+
56
+ """
30
57
  executor = PruningThreadPoolExecutor(1)
31
58
  coro = executor.run(time.sleep, 0.1)
32
59
  assert asyncio.iscoroutine(coro)
@@ -35,7 +62,21 @@ async def test_pruning_thread_pool_executor_run():
35
62
 
36
63
  @pytest.mark.asyncio
37
64
  async def test_process_pool_executor_submit():
38
- """Tests the ProcessPoolExecutor by submitting the work function asynchronously."""
65
+ """Tests the :class:`ProcessPoolExecutor` by submitting the work function.
66
+
67
+ This test ensures that the `submit` method of the `ProcessPoolExecutor` returns an
68
+ :class:`asyncio.Future` when executed with a synchronous function.
69
+
70
+ Note:
71
+ The `submit` method in this context returns an `asyncio.Future`, not a `concurrent.futures.Future`.
72
+ This is specific to the implementation of the executors in the `a_sync` library, which adapts the
73
+ behavior to integrate with the asyncio event loop.
74
+
75
+ See Also:
76
+ - :meth:`ProcessPoolExecutor.submit`
77
+ - :class:`asyncio.Future`
78
+
79
+ """
39
80
  executor = ProcessPoolExecutor(1)
40
81
  fut = executor.submit(time.sleep, 0.1)
41
82
  assert isinstance(fut, asyncio.Future)
@@ -44,7 +85,21 @@ async def test_process_pool_executor_submit():
44
85
 
45
86
  @pytest.mark.asyncio
46
87
  async def test_thread_pool_executor_submit():
47
- """Tests the ThreadPoolExecutor by submitting the work function asynchronously."""
88
+ """Tests the :class:`ThreadPoolExecutor` by submitting the work function.
89
+
90
+ This test ensures that the `submit` method of the `ThreadPoolExecutor` returns an
91
+ :class:`asyncio.Future` when executed with a synchronous function.
92
+
93
+ Note:
94
+ The `submit` method in this context returns an `asyncio.Future`, not a `concurrent.futures.Future`.
95
+ This is specific to the implementation of the executors in the `a_sync` library, which adapts the
96
+ behavior to integrate with the asyncio event loop.
97
+
98
+ See Also:
99
+ - :meth:`ThreadPoolExecutor.submit`
100
+ - :class:`asyncio.Future`
101
+
102
+ """
48
103
  executor = ThreadPoolExecutor(1)
49
104
  fut = executor.submit(time.sleep, 0.1)
50
105
  assert isinstance(fut, asyncio.Future)
@@ -53,7 +108,21 @@ async def test_thread_pool_executor_submit():
53
108
 
54
109
  @pytest.mark.asyncio
55
110
  async def test_pruning_thread_pool_executor_submit():
56
- """Tests the PruningThreadPoolExecutor by submitting the work function asynchronously."""
111
+ """Tests the :class:`PruningThreadPoolExecutor` by submitting the work function.
112
+
113
+ This test ensures that the `submit` method of the `PruningThreadPoolExecutor` returns an
114
+ :class:`asyncio.Future` when executed with a synchronous function.
115
+
116
+ Note:
117
+ The `submit` method in this context returns an `asyncio.Future`, not a `concurrent.futures.Future`.
118
+ This is specific to the implementation of the executors in the `a_sync` library, which adapts the
119
+ behavior to integrate with the asyncio event loop.
120
+
121
+ See Also:
122
+ - :meth:`PruningThreadPoolExecutor.submit`
123
+ - :class:`asyncio.Future`
124
+
125
+ """
57
126
  executor = PruningThreadPoolExecutor(1)
58
127
  fut = executor.submit(time.sleep, 0.1)
59
128
  assert isinstance(fut, asyncio.Future)
@@ -62,7 +131,16 @@ async def test_pruning_thread_pool_executor_submit():
62
131
 
63
132
  @pytest.mark.asyncio
64
133
  async def test_process_pool_executor_sync_run():
65
- """Tests the ProcessPoolExecutor by running and submitting the work function synchronously."""
134
+ """Tests the :class:`ProcessPoolExecutor` by running and submitting the work function synchronously.
135
+
136
+ This test ensures that the `run` method of the `ProcessPoolExecutor` returns a coroutine
137
+ when executed with a synchronous function.
138
+
139
+ See Also:
140
+ - :meth:`ProcessPoolExecutor.run`
141
+ - :func:`asyncio.iscoroutine`
142
+
143
+ """
66
144
  executor = ProcessPoolExecutor(0)
67
145
  coro = executor.run(time.sleep, 0.1)
68
146
  assert asyncio.iscoroutine(coro)
@@ -71,7 +149,16 @@ async def test_process_pool_executor_sync_run():
71
149
 
72
150
  @pytest.mark.asyncio
73
151
  async def test_thread_pool_executor_sync_run():
74
- """Tests the ThreadPoolExecutor by running and submitting the work function synchronously."""
152
+ """Tests the :class:`ThreadPoolExecutor` by running and submitting the work function synchronously.
153
+
154
+ This test ensures that the `run` method of the `ThreadPoolExecutor` returns a coroutine
155
+ when executed with a synchronous function.
156
+
157
+ See Also:
158
+ - :meth:`ThreadPoolExecutor.run`
159
+ - :func:`asyncio.iscoroutine`
160
+
161
+ """
75
162
  executor = ThreadPoolExecutor(0)
76
163
  coro = executor.run(time.sleep, 0.1)
77
164
  assert asyncio.iscoroutine(coro)
@@ -80,7 +167,16 @@ async def test_thread_pool_executor_sync_run():
80
167
 
81
168
  @pytest.mark.asyncio
82
169
  async def test_pruning_thread_pool_executor_sync_run():
83
- """Tests the PruningThreadPoolExecutor by running and submitting the work function synchronously."""
170
+ """Tests the :class:`PruningThreadPoolExecutor` by running and submitting the work function synchronously.
171
+
172
+ This test ensures that the `run` method of the `PruningThreadPoolExecutor` returns a coroutine
173
+ when executed with a synchronous function.
174
+
175
+ See Also:
176
+ - :meth:`PruningThreadPoolExecutor.run`
177
+ - :func:`asyncio.iscoroutine`
178
+
179
+ """
84
180
  executor = PruningThreadPoolExecutor(0)
85
181
  coro = executor.run(time.sleep, 0.1)
86
182
  assert asyncio.iscoroutine(coro)
@@ -89,7 +185,21 @@ async def test_pruning_thread_pool_executor_sync_run():
89
185
 
90
186
  @pytest.mark.asyncio
91
187
  async def test_process_pool_executor_sync_submit():
92
- """Tests the ProcessPoolExecutor by submitting the work function synchronously."""
188
+ """Tests the :class:`ProcessPoolExecutor` by submitting the work function synchronously.
189
+
190
+ This test ensures that the `submit` method of the `ProcessPoolExecutor` returns an
191
+ :class:`asyncio.Future` when executed with a synchronous function.
192
+
193
+ Note:
194
+ The `submit` method in this context returns an `asyncio.Future`, not a `concurrent.futures.Future`.
195
+ This is specific to the implementation of the executors in the `a_sync` library, which adapts the
196
+ behavior to integrate with the asyncio event loop.
197
+
198
+ See Also:
199
+ - :meth:`ProcessPoolExecutor.submit`
200
+ - :class:`asyncio.Future`
201
+
202
+ """
93
203
  executor = ProcessPoolExecutor(0)
94
204
  fut = executor.submit(time.sleep, 0.1)
95
205
  assert isinstance(fut, asyncio.Future)
@@ -98,7 +208,21 @@ async def test_process_pool_executor_sync_submit():
98
208
 
99
209
  @pytest.mark.asyncio
100
210
  async def test_thread_pool_executor_sync_submit():
101
- """Tests the ThreadPoolExecutor by submitting the work function synchronously."""
211
+ """Tests the :class:`ThreadPoolExecutor` by submitting the work function synchronously.
212
+
213
+ This test ensures that the `submit` method of the `ThreadPoolExecutor` returns an
214
+ :class:`asyncio.Future` when executed with a synchronous function.
215
+
216
+ Note:
217
+ The `submit` method in this context returns an `asyncio.Future`, not a `concurrent.futures.Future`.
218
+ This is specific to the implementation of the executors in the `a_sync` library, which adapts the
219
+ behavior to integrate with the asyncio event loop.
220
+
221
+ See Also:
222
+ - :meth:`ThreadPoolExecutor.submit`
223
+ - :class:`asyncio.Future`
224
+
225
+ """
102
226
  executor = ThreadPoolExecutor(0)
103
227
  fut = executor.submit(time.sleep, 0.1)
104
228
  assert isinstance(fut, asyncio.Future)
@@ -107,7 +231,21 @@ async def test_thread_pool_executor_sync_submit():
107
231
 
108
232
  @pytest.mark.asyncio
109
233
  async def test_pruning_thread_pool_executor_sync_submit():
110
- """Tests the PruningThreadPoolExecutor by submitting the work function synchronously."""
234
+ """Tests the :class:`PruningThreadPoolExecutor` by submitting the work function synchronously.
235
+
236
+ This test ensures that the `submit` method of the `PruningThreadPoolExecutor` returns an
237
+ :class:`asyncio.Future` when executed with a synchronous function.
238
+
239
+ Note:
240
+ The `submit` method in this context returns an `asyncio.Future`, not a `concurrent.futures.Future`.
241
+ This is specific to the implementation of the executors in the `a_sync` library, which adapts the
242
+ behavior to integrate with the asyncio event loop.
243
+
244
+ See Also:
245
+ - :meth:`PruningThreadPoolExecutor.submit`
246
+ - :class:`asyncio.Future`
247
+
248
+ """
111
249
  executor = PruningThreadPoolExecutor(0)
112
250
  fut = executor.submit(time.sleep, 0.1)
113
251
  assert isinstance(fut, asyncio.Future)
tests/test_abstract.py CHANGED
@@ -13,6 +13,21 @@ else:
13
13
 
14
14
 
15
15
  def test_abc_direct_init():
16
+ """Test that ASyncABC cannot be instantiated directly.
17
+
18
+ This test verifies that attempting to instantiate the abstract base class
19
+ `ASyncABC` raises a `TypeError`. This is expected behavior for abstract
20
+ base classes in Python, which require subclasses to implement all abstract
21
+ methods before instantiation.
22
+
23
+ Raises:
24
+ TypeError: If `ASyncABC` is instantiated without implementing abstract methods.
25
+
26
+ Example:
27
+ >>> from a_sync.a_sync.abstract import ASyncABC
28
+ >>> ASyncABC()
29
+ TypeError: Can't instantiate abstract class ASyncABC with abstract methods ...
30
+ """
16
31
  with pytest.raises(
17
32
  TypeError,
18
33
  match=f"Can't instantiate abstract class ASyncABC {_MIDDLE} {', '.join(_methods)}",
tests/test_base.py CHANGED
@@ -19,6 +19,21 @@ from tests.fixtures import (
19
19
 
20
20
 
21
21
  def test_base_direct_init():
22
+ """Test direct initialization of :class:`~a_sync.a_sync.base.ASyncGenericBase`.
23
+
24
+ This test ensures that directly initializing :class:`~a_sync.a_sync.base.ASyncGenericBase`
25
+ raises a :class:`NotImplementedError`, as it is intended to be subclassed.
26
+
27
+ Raises:
28
+ NotImplementedError: If :class:`~a_sync.a_sync.base.ASyncGenericBase` is directly initialized.
29
+
30
+ Example:
31
+ >>> from a_sync.a_sync.base import ASyncGenericBase
32
+ >>> ASyncGenericBase()
33
+ Traceback (most recent call last):
34
+ ...
35
+ NotImplementedError: You should not create instances of `ASyncGenericBase` directly, you should subclass `ASyncGenericBase` instead.
36
+ """
22
37
  with pytest.raises(NotImplementedError, match=""):
23
38
  ASyncGenericBase()
24
39
 
@@ -31,6 +46,27 @@ both_modes = pytest.mark.parametrize("sync", [True, False])
31
46
  @classes
32
47
  @both_modes
33
48
  def test_inheritance(cls, sync: bool):
49
+ """Test inheritance and metaclass functionality.
50
+
51
+ This test checks that instances of subclasses of :class:`~a_sync.a_sync.base.ASyncGenericBase`
52
+ are correctly initialized with the :class:`~a_sync.a_sync._meta.ASyncMeta` metaclass, ensuring
53
+ that methods are wrapped with asynchronous or synchronous behavior based on flags.
54
+
55
+ Args:
56
+ cls: The class to test.
57
+ sync: Whether to test in synchronous mode.
58
+
59
+ Example:
60
+ >>> class MyClass(ASyncGenericBase):
61
+ ... def __init__(self, value, sync=True):
62
+ ... self.value = value
63
+ ... self.sync = sync
64
+ >>> instance = MyClass(1, sync=True)
65
+ >>> isinstance(instance, ASyncGenericBase)
66
+ True
67
+ >>> isinstance(instance.__class__, ASyncMeta)
68
+ True
69
+ """
34
70
  instance = cls(1, sync=sync)
35
71
  assert isinstance(instance, ASyncGenericBase)
36
72
  assert isinstance(instance.__class__, ASyncMeta)
@@ -39,6 +75,18 @@ def test_inheritance(cls, sync: bool):
39
75
  @classes
40
76
  @increment
41
77
  def test_method_sync(cls: type, i: int):
78
+ """Test synchronous method execution.
79
+
80
+ This test verifies that methods in synchronous instances of :class:`~a_sync.a_sync.base.ASyncGenericBase`
81
+ subclasses execute correctly and can be overridden with keyword arguments to run asynchronously.
82
+
83
+ Args:
84
+ cls: The class to test.
85
+ i: The increment value for testing.
86
+
87
+ Raises:
88
+ WrongThreadError: If a synchronous method is incorrectly awaited in a :class:`TestSync` instance.
89
+ """
42
90
  sync_instance = cls(i, sync=True)
43
91
  assert sync_instance.test_fn() == i
44
92
 
@@ -76,6 +124,19 @@ def test_method_sync(cls: type, i: int):
76
124
  @increment
77
125
  @pytest.mark.asyncio_cooperative
78
126
  async def test_method_async(cls: type, i: int):
127
+ """Test asynchronous method execution.
128
+
129
+ This test verifies that methods in asynchronous instances of :class:`~a_sync.a_sync.base.ASyncGenericBase`
130
+ subclasses execute correctly and can be overridden with keyword arguments to run synchronously.
131
+
132
+ Args:
133
+ cls: The class to test.
134
+ i: The increment value for testing.
135
+
136
+ Raises:
137
+ WrongThreadError: If an asynchronous method is incorrectly run synchronously in a :class:`TestSync` instance.
138
+ SyncModeInAsyncContextError: If a synchronous method is run in an asynchronous context.
139
+ """
79
140
  async_instance = cls(i, sync=False)
80
141
  if isinstance(async_instance, TestSync):
81
142
  # this raises an assertion error inside of the test_fn execution. this is okay.
@@ -105,6 +166,15 @@ async def test_method_async(cls: type, i: int):
105
166
  @classes
106
167
  @increment
107
168
  def test_property_sync(cls: type, i: int):
169
+ """Test synchronous property access.
170
+
171
+ This test verifies that properties in synchronous instances of :class:`~a_sync.a_sync.base.ASyncGenericBase`
172
+ subclasses are accessed correctly and that hidden methods for properties can be accessed.
173
+
174
+ Args:
175
+ cls: The class to test.
176
+ i: The increment value for testing.
177
+ """
108
178
  sync_instance = cls(i, sync=True)
109
179
  assert sync_instance.test_property == i * 2
110
180
 
@@ -120,6 +190,18 @@ def test_property_sync(cls: type, i: int):
120
190
  @increment
121
191
  @pytest.mark.asyncio_cooperative
122
192
  async def test_property_async(cls: type, i: int):
193
+ """Test asynchronous property access.
194
+
195
+ This test verifies that properties in asynchronous instances of :class:`~a_sync.a_sync.base.ASyncGenericBase`
196
+ subclasses are accessed correctly and that hidden methods for properties can be accessed.
197
+
198
+ Args:
199
+ cls: The class to test.
200
+ i: The increment value for testing.
201
+
202
+ Raises:
203
+ SyncModeInAsyncContextError: If a synchronous property is accessed in an asynchronous context.
204
+ """
123
205
  async_instance = cls(i, sync=False)
124
206
  assert await async_instance.test_property == i * 2
125
207
 
@@ -136,6 +218,15 @@ async def test_property_async(cls: type, i: int):
136
218
  @classes
137
219
  @increment
138
220
  def test_cached_property_sync(cls: type, i: int):
221
+ """Test synchronous cached property access.
222
+
223
+ This test verifies that cached properties in synchronous instances of :class:`~a_sync.a_sync.base.ASyncGenericBase`
224
+ subclasses are accessed correctly and that hidden methods for cached properties can be accessed.
225
+
226
+ Args:
227
+ cls: The class to test.
228
+ i: The increment value for testing.
229
+ """
139
230
  sync_instance = cls(i, sync=True)
140
231
  start = time.time()
141
232
  assert sync_instance.test_cached_property == i * 3
@@ -170,6 +261,18 @@ def test_cached_property_sync(cls: type, i: int):
170
261
  @increment
171
262
  @pytest.mark.asyncio_cooperative
172
263
  async def test_cached_property_async(cls: type, i: int):
264
+ """Test asynchronous cached property access.
265
+
266
+ This test verifies that cached properties in asynchronous instances of :class:`~a_sync.a_sync.base.ASyncGenericBase`
267
+ subclasses are accessed correctly and that hidden methods for cached properties can be accessed.
268
+
269
+ Args:
270
+ cls: The class to test.
271
+ i: The increment value for testing.
272
+
273
+ Raises:
274
+ SyncModeInAsyncContextError: If a synchronous cached property is accessed in an asynchronous context.
275
+ """
173
276
  async_instance = cls(i, sync=False)
174
277
  start = time.time()
175
278
  assert await async_instance.test_cached_property == i * 3
@@ -197,6 +300,22 @@ async def test_cached_property_async(cls: type, i: int):
197
300
 
198
301
  @pytest.mark.asyncio_cooperative
199
302
  async def test_asynchronous_context_manager():
303
+ """Test asynchronous context manager functionality.
304
+
305
+ This test verifies that :class:`~a_sync.a_sync.base.ASyncGenericBase` subclasses can be used as asynchronous context managers.
306
+
307
+ Example:
308
+ >>> class AsyncContextManager(ASyncGenericBase):
309
+ ... async def __aenter__(self):
310
+ ... self.entered = True
311
+ ... return self
312
+ ... async def __aexit__(self, exc_type, exc_val, exc_tb):
313
+ ... self.exited = True
314
+ >>> async with AsyncContextManager() as cm:
315
+ ... assert cm.entered
316
+ >>> assert cm.exited
317
+ """
318
+
200
319
  # Can the implementation work with an async context manager?
201
320
  class AsyncContextManager(ASyncGenericBase):
202
321
  async def __aenter__(self):
@@ -212,6 +331,21 @@ async def test_asynchronous_context_manager():
212
331
 
213
332
 
214
333
  def test_synchronous_context_manager():
334
+ """Test synchronous context manager functionality.
335
+
336
+ This test verifies that :class:`~a_sync.a_sync.base.ASyncGenericBase` subclasses can be used as synchronous context managers.
337
+
338
+ Example:
339
+ >>> class SyncContextManager(ASyncGenericBase):
340
+ ... def __enter__(self):
341
+ ... self.entered = True
342
+ ... return self
343
+ ... def __exit__(self, exc_type, exc_val, exc_tb):
344
+ ... self.exited = True
345
+ >>> with SyncContextManager() as cm:
346
+ ... assert cm.entered
347
+ >>> assert cm.exited
348
+ """
215
349
  # Can the implementation work with a context manager?
216
350
 
217
351
  class SyncContextManager(ASyncGenericBase):
@@ -229,6 +363,24 @@ def test_synchronous_context_manager():
229
363
 
230
364
  @pytest.mark.asyncio_cooperative
231
365
  async def test_asynchronous_iteration():
366
+ """Test asynchronous iteration functionality.
367
+
368
+ This test verifies that :class:`~a_sync.a_sync.base.ASyncGenericBase` subclasses can be used with asynchronous iteration.
369
+
370
+ Example:
371
+ >>> class ASyncObjectWithAiter(ASyncGenericBase):
372
+ ... def __init__(self):
373
+ ... self.count = 0
374
+ ... def __aiter__(self):
375
+ ... return self
376
+ ... async def __anext__(self):
377
+ ... if self.count < 3:
378
+ ... self.count += 1
379
+ ... return self.count
380
+ ... raise StopAsyncIteration
381
+ >>> assert [item async for item in ASyncObjectWithAiter()] == [1, 2, 3]
382
+ """
383
+
232
384
  # Does the implementation screw anything up with aiteration?
233
385
  class ASyncObjectWithAiter(ASyncGenericBase):
234
386
  def __init__(self):
@@ -247,6 +399,24 @@ async def test_asynchronous_iteration():
247
399
 
248
400
 
249
401
  def test_synchronous_iteration():
402
+ """Test synchronous iteration functionality.
403
+
404
+ This test verifies that :class:`~a_sync.a_sync.base.ASyncGenericBase` subclasses can be used with synchronous iteration.
405
+
406
+ Example:
407
+ >>> class ASyncObjectWithIter(ASyncGenericBase):
408
+ ... def __init__(self):
409
+ ... self.count = 0
410
+ ... def __iter__(self):
411
+ ... return self
412
+ ... def __next__(self):
413
+ ... if self.count < 3:
414
+ ... self.count += 1
415
+ ... return self.count
416
+ ... raise StopIteration
417
+ >>> assert list(ASyncObjectWithIter()) == [1, 2, 3]
418
+ """
419
+
250
420
  # Does the implementation screw anything up with iteration?
251
421
  class ASyncObjectWithIter(ASyncGenericBase):
252
422
  def __init__(self):
@@ -272,13 +442,39 @@ class ClassWithGenFunc(ASyncGenericBase):
272
442
 
273
443
 
274
444
  def test_bound_generator_meta_sync():
275
- """Does the metaclass handle generator functions correctly?"""
445
+ """Test synchronous generator function handling.
446
+
447
+ This test verifies that the :class:`~a_sync.a_sync._meta.ASyncMeta` metaclass correctly handles generator functions
448
+ when used in synchronous contexts.
449
+
450
+ Example:
451
+ >>> class ClassWithGenFunc(ASyncGenericBase):
452
+ ... async def generate(self):
453
+ ... yield 0
454
+ ... yield 1
455
+ ... yield 2
456
+ >>> for _ in ClassWithGenFunc().generate():
457
+ ... assert isinstance(_, int)
458
+ """
276
459
  for _ in ClassWithGenFunc().generate():
277
460
  assert isinstance(_, int)
278
461
 
279
462
 
280
463
  @pytest.mark.asyncio_cooperative
281
464
  async def test_bound_generator_meta_async():
282
- """Does the metaclass handle generator functions correctly?"""
465
+ """Test asynchronous generator function handling.
466
+
467
+ This test verifies that the :class:`~a_sync.a_sync._meta.ASyncMeta` metaclass correctly handles generator functions
468
+ when used in asynchronous contexts.
469
+
470
+ Example:
471
+ >>> class ClassWithGenFunc(ASyncGenericBase):
472
+ ... async def generate(self):
473
+ ... yield 0
474
+ ... yield 1
475
+ ... yield 2
476
+ >>> async for _ in ClassWithGenFunc().generate():
477
+ ... assert isinstance(_, int)
478
+ """
283
479
  async for _ in ClassWithGenFunc().generate():
284
480
  assert isinstance(_, int)
tests/test_executor.py CHANGED
@@ -7,12 +7,35 @@ from a_sync.executor import AsyncProcessPoolExecutor, ProcessPoolExecutor
7
7
 
8
8
 
9
9
  def do_work(i, kwarg=None):
10
+ """Performs work by sleeping for a specified duration.
11
+
12
+ Args:
13
+ i (int): The duration to sleep.
14
+ kwarg (optional): An optional keyword argument to assert against `i`.
15
+
16
+ Raises:
17
+ AssertionError: If `kwarg` is provided and does not equal `i`.
18
+ """
10
19
  time.sleep(i)
11
20
  if kwarg:
12
21
  assert kwarg == i
13
22
 
14
23
 
15
24
  def test_executor():
25
+ """Tests the functionality of the ProcessPoolExecutor.
26
+
27
+ This test verifies that the ProcessPoolExecutor behaves as expected,
28
+ including running tasks, handling futures, and managing exceptions.
29
+
30
+ Note:
31
+ `ProcessPoolExecutor` is an alias for `AsyncProcessPoolExecutor`,
32
+ which is why the assertion `assert isinstance(executor, AsyncProcessPoolExecutor)` is always true.
33
+
34
+ See Also:
35
+ - :class:`a_sync.executor.AsyncProcessPoolExecutor`
36
+ - :meth:`a_sync.executor._AsyncExecutorMixin.run`
37
+ - :meth:`a_sync.executor._AsyncExecutorMixin.submit`
38
+ """
16
39
  executor = ProcessPoolExecutor(1)
17
40
  assert isinstance(executor, AsyncProcessPoolExecutor)
18
41
  coro = executor.run(do_work, 3)
tests/test_singleton.py CHANGED
@@ -2,7 +2,19 @@ from a_sync.a_sync.singleton import ASyncGenericSingleton
2
2
 
3
3
 
4
4
  def test_flag_predefined():
5
- """We had a failure case where the subclass implementation assigned the flag value to the class and did not allow user to determine at init time"""
5
+ """Test predefined sync flag in ASyncGenericSingleton subclasses.
6
+
7
+ This test verifies that the `sync` flag can be predefined in a subclass of
8
+ `ASyncGenericSingleton` and that it allows the user to determine the sync/async
9
+ mode at initialization time. It checks if the `sync` attribute can be set to
10
+ `True` or `False` in subclasses and whether instances can be created without errors.
11
+
12
+ The test ensures that the `sync` flag is correctly interpreted and does not
13
+ interfere with the instantiation of subclasses.
14
+
15
+ See Also:
16
+ - :class:`a_sync.a_sync.singleton.ASyncGenericSingleton` for more details on the base class.
17
+ """
6
18
 
7
19
  class Test(ASyncGenericSingleton):
8
20
  sync = True