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.
- a_sync/ENVIRONMENT_VARIABLES.py +34 -3
- a_sync/__init__.py +32 -9
- a_sync/_smart.py +105 -6
- a_sync/_typing.py +56 -3
- a_sync/a_sync/_descriptor.py +174 -12
- a_sync/a_sync/_flags.py +64 -3
- a_sync/a_sync/_helpers.py +40 -8
- a_sync/a_sync/_kwargs.py +30 -6
- a_sync/a_sync/_meta.py +35 -6
- a_sync/a_sync/abstract.py +57 -9
- a_sync/a_sync/config.py +44 -7
- a_sync/a_sync/decorator.py +217 -37
- a_sync/a_sync/function.py +339 -47
- a_sync/a_sync/method.py +241 -52
- a_sync/a_sync/modifiers/__init__.py +39 -1
- a_sync/a_sync/modifiers/cache/__init__.py +75 -5
- a_sync/a_sync/modifiers/cache/memory.py +50 -6
- a_sync/a_sync/modifiers/limiter.py +55 -6
- a_sync/a_sync/modifiers/manager.py +46 -2
- a_sync/a_sync/modifiers/semaphores.py +84 -11
- a_sync/a_sync/singleton.py +43 -19
- a_sync/asyncio/__init__.py +137 -1
- a_sync/asyncio/as_completed.py +44 -38
- a_sync/asyncio/create_task.py +46 -10
- a_sync/asyncio/gather.py +72 -25
- a_sync/exceptions.py +178 -11
- a_sync/executor.py +51 -3
- a_sync/future.py +671 -29
- a_sync/iter.py +64 -7
- a_sync/primitives/_debug.py +59 -5
- a_sync/primitives/_loggable.py +36 -6
- a_sync/primitives/locks/counter.py +74 -7
- a_sync/primitives/locks/prio_semaphore.py +87 -8
- a_sync/primitives/locks/semaphore.py +68 -20
- a_sync/primitives/queue.py +65 -26
- a_sync/task.py +51 -15
- a_sync/utils/iterators.py +52 -16
- {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
- ez_a_sync-0.22.16.dist-info/RECORD +74 -0
- {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
- tests/executor.py +150 -12
- tests/test_abstract.py +15 -0
- tests/test_base.py +198 -2
- tests/test_executor.py +23 -0
- tests/test_singleton.py +13 -1
- tests/test_task.py +45 -17
- ez_a_sync-0.22.15.dist-info/RECORD +0 -74
- {ez_a_sync-0.22.15.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
- {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
|
|
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
|
|
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
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|