ez-a-sync 0.22.14__py3-none-any.whl → 0.22.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ez-a-sync might be problematic. Click here for more details.
- a_sync/ENVIRONMENT_VARIABLES.py +37 -5
- a_sync/__init__.py +53 -12
- a_sync/_smart.py +231 -28
- a_sync/_typing.py +112 -15
- a_sync/a_sync/__init__.py +35 -10
- a_sync/a_sync/_descriptor.py +248 -38
- a_sync/a_sync/_flags.py +78 -9
- a_sync/a_sync/_helpers.py +46 -13
- a_sync/a_sync/_kwargs.py +33 -8
- a_sync/a_sync/_meta.py +149 -28
- a_sync/a_sync/abstract.py +150 -28
- a_sync/a_sync/base.py +34 -16
- a_sync/a_sync/config.py +85 -14
- a_sync/a_sync/decorator.py +441 -139
- a_sync/a_sync/function.py +709 -147
- a_sync/a_sync/method.py +437 -110
- a_sync/a_sync/modifiers/__init__.py +85 -5
- a_sync/a_sync/modifiers/cache/__init__.py +116 -17
- a_sync/a_sync/modifiers/cache/memory.py +130 -20
- a_sync/a_sync/modifiers/limiter.py +101 -22
- a_sync/a_sync/modifiers/manager.py +142 -16
- a_sync/a_sync/modifiers/semaphores.py +121 -15
- a_sync/a_sync/property.py +383 -82
- a_sync/a_sync/singleton.py +44 -19
- a_sync/aliases.py +0 -1
- a_sync/asyncio/__init__.py +140 -1
- a_sync/asyncio/as_completed.py +213 -79
- a_sync/asyncio/create_task.py +70 -20
- a_sync/asyncio/gather.py +125 -58
- a_sync/asyncio/utils.py +3 -3
- a_sync/exceptions.py +248 -26
- a_sync/executor.py +164 -69
- a_sync/future.py +1227 -168
- a_sync/iter.py +173 -56
- a_sync/primitives/__init__.py +14 -2
- a_sync/primitives/_debug.py +72 -18
- a_sync/primitives/_loggable.py +41 -10
- a_sync/primitives/locks/__init__.py +5 -2
- a_sync/primitives/locks/counter.py +107 -38
- a_sync/primitives/locks/event.py +21 -7
- a_sync/primitives/locks/prio_semaphore.py +262 -63
- a_sync/primitives/locks/semaphore.py +138 -89
- a_sync/primitives/queue.py +601 -60
- a_sync/sphinx/__init__.py +0 -1
- a_sync/sphinx/ext.py +160 -50
- a_sync/task.py +313 -112
- a_sync/utils/__init__.py +12 -6
- a_sync/utils/iterators.py +170 -50
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/METADATA +1 -1
- ez_a_sync-0.22.16.dist-info/RECORD +74 -0
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/WHEEL +1 -1
- tests/conftest.py +1 -2
- tests/executor.py +250 -9
- tests/fixtures.py +61 -32
- tests/test_abstract.py +22 -4
- tests/test_as_completed.py +54 -21
- tests/test_base.py +264 -19
- tests/test_cache.py +31 -15
- tests/test_decorator.py +54 -28
- tests/test_executor.py +31 -13
- tests/test_future.py +45 -8
- tests/test_gather.py +8 -2
- tests/test_helpers.py +2 -0
- tests/test_iter.py +55 -13
- tests/test_limiter.py +5 -3
- tests/test_meta.py +23 -9
- tests/test_modified.py +4 -1
- tests/test_semaphore.py +15 -8
- tests/test_singleton.py +28 -11
- tests/test_task.py +162 -36
- ez_a_sync-0.22.14.dist-info/RECORD +0 -74
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.14.dist-info → ez_a_sync-0.22.16.dist-info}/top_level.txt +0 -0
tests/test_base.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import asyncio
|
|
3
2
|
import time
|
|
4
3
|
|
|
@@ -9,31 +8,88 @@ from a_sync.a_sync.base import ASyncGenericBase
|
|
|
9
8
|
from a_sync.a_sync._meta import ASyncMeta
|
|
10
9
|
from a_sync.a_sync.method import ASyncBoundMethodAsyncDefault
|
|
11
10
|
from a_sync.exceptions import SyncModeInAsyncContextError
|
|
12
|
-
from tests.fixtures import
|
|
11
|
+
from tests.fixtures import (
|
|
12
|
+
TestClass,
|
|
13
|
+
TestInheritor,
|
|
14
|
+
TestMeta,
|
|
15
|
+
increment,
|
|
16
|
+
TestSync,
|
|
17
|
+
WrongThreadError,
|
|
18
|
+
)
|
|
13
19
|
|
|
14
20
|
|
|
15
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
|
+
"""
|
|
16
37
|
with pytest.raises(NotImplementedError, match=""):
|
|
17
38
|
ASyncGenericBase()
|
|
18
39
|
|
|
19
40
|
|
|
20
|
-
classes = pytest.mark.parametrize(
|
|
41
|
+
classes = pytest.mark.parametrize("cls", [TestClass, TestSync, TestInheritor, TestMeta])
|
|
42
|
+
|
|
43
|
+
both_modes = pytest.mark.parametrize("sync", [True, False])
|
|
21
44
|
|
|
22
|
-
both_modes = pytest.mark.parametrize('sync', [True, False])
|
|
23
45
|
|
|
24
46
|
@classes
|
|
25
47
|
@both_modes
|
|
26
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
|
+
"""
|
|
27
70
|
instance = cls(1, sync=sync)
|
|
28
71
|
assert isinstance(instance, ASyncGenericBase)
|
|
29
72
|
assert isinstance(instance.__class__, ASyncMeta)
|
|
30
73
|
|
|
74
|
+
|
|
31
75
|
@classes
|
|
32
76
|
@increment
|
|
33
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
|
+
"""
|
|
34
90
|
sync_instance = cls(i, sync=True)
|
|
35
91
|
assert sync_instance.test_fn() == i
|
|
36
|
-
|
|
92
|
+
|
|
37
93
|
# Can we override with kwargs?
|
|
38
94
|
assert sync_instance.test_fn(sync=True) == i
|
|
39
95
|
assert sync_instance.test_fn(asynchronous=False) == i
|
|
@@ -42,23 +98,51 @@ def test_method_sync(cls: type, i: int):
|
|
|
42
98
|
if isinstance(sync_instance, TestSync):
|
|
43
99
|
# this raises an assertion error inside of the test_fn execution. this is okay.
|
|
44
100
|
with pytest.raises(WrongThreadError):
|
|
45
|
-
asyncio.get_event_loop().run_until_complete(
|
|
101
|
+
asyncio.get_event_loop().run_until_complete(
|
|
102
|
+
sync_instance.test_fn(sync=False)
|
|
103
|
+
)
|
|
46
104
|
with pytest.raises(WrongThreadError):
|
|
47
|
-
asyncio.get_event_loop().run_until_complete(
|
|
105
|
+
asyncio.get_event_loop().run_until_complete(
|
|
106
|
+
sync_instance.test_fn(asynchronous=True)
|
|
107
|
+
)
|
|
48
108
|
else:
|
|
49
|
-
assert isinstance(
|
|
50
|
-
|
|
109
|
+
assert isinstance(
|
|
110
|
+
asyncio.get_event_loop().run_until_complete(
|
|
111
|
+
sync_instance.test_fn(sync=False)
|
|
112
|
+
),
|
|
113
|
+
int,
|
|
114
|
+
)
|
|
115
|
+
assert isinstance(
|
|
116
|
+
asyncio.get_event_loop().run_until_complete(
|
|
117
|
+
sync_instance.test_fn(asynchronous=True)
|
|
118
|
+
),
|
|
119
|
+
int,
|
|
120
|
+
)
|
|
121
|
+
|
|
51
122
|
|
|
52
123
|
@classes
|
|
53
124
|
@increment
|
|
54
125
|
@pytest.mark.asyncio_cooperative
|
|
55
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
|
+
"""
|
|
56
140
|
async_instance = cls(i, sync=False)
|
|
57
141
|
if isinstance(async_instance, TestSync):
|
|
58
142
|
# this raises an assertion error inside of the test_fn execution. this is okay.
|
|
59
143
|
with pytest.raises(WrongThreadError):
|
|
60
144
|
assert await async_instance.test_fn() == i
|
|
61
|
-
|
|
145
|
+
|
|
62
146
|
# Can we override with kwargs?
|
|
63
147
|
with pytest.raises(WrongThreadError):
|
|
64
148
|
async_instance.test_fn(sync=True)
|
|
@@ -82,9 +166,18 @@ async def test_method_async(cls: type, i: int):
|
|
|
82
166
|
@classes
|
|
83
167
|
@increment
|
|
84
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
|
+
"""
|
|
85
178
|
sync_instance = cls(i, sync=True)
|
|
86
179
|
assert sync_instance.test_property == i * 2
|
|
87
|
-
|
|
180
|
+
|
|
88
181
|
# Can we access hidden methods for properties?
|
|
89
182
|
getter = sync_instance.__test_property__
|
|
90
183
|
assert isinstance(getter, HiddenMethod), getter
|
|
@@ -92,10 +185,23 @@ def test_property_sync(cls: type, i: int):
|
|
|
92
185
|
assert asyncio.iscoroutine(getter_coro), getter_coro
|
|
93
186
|
assert asyncio.get_event_loop().run_until_complete(getter_coro) == i * 2
|
|
94
187
|
|
|
188
|
+
|
|
95
189
|
@classes
|
|
96
190
|
@increment
|
|
97
191
|
@pytest.mark.asyncio_cooperative
|
|
98
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
|
+
"""
|
|
99
205
|
async_instance = cls(i, sync=False)
|
|
100
206
|
assert await async_instance.test_property == i * 2
|
|
101
207
|
|
|
@@ -112,12 +218,23 @@ async def test_property_async(cls: type, i: int):
|
|
|
112
218
|
@classes
|
|
113
219
|
@increment
|
|
114
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
|
+
"""
|
|
115
230
|
sync_instance = cls(i, sync=True)
|
|
116
231
|
start = time.time()
|
|
117
232
|
assert sync_instance.test_cached_property == i * 3
|
|
118
233
|
assert isinstance(sync_instance.test_cached_property, int)
|
|
119
234
|
duration = time.time() - start
|
|
120
|
-
assert
|
|
235
|
+
assert (
|
|
236
|
+
duration < 3
|
|
237
|
+
), "There is a 2 second sleep in 'test_cached_property' but it should only run once."
|
|
121
238
|
|
|
122
239
|
# Can we access hidden methods for properties?
|
|
123
240
|
start = time.time()
|
|
@@ -128,14 +245,34 @@ def test_cached_property_sync(cls: type, i: int):
|
|
|
128
245
|
assert asyncio.get_event_loop().run_until_complete(getter_coro) == i * 3
|
|
129
246
|
|
|
130
247
|
# Can we override them too?
|
|
131
|
-
assert
|
|
248
|
+
assert (
|
|
249
|
+
asyncio.get_event_loop().run_until_complete(
|
|
250
|
+
sync_instance.__test_cached_property__(sync=False)
|
|
251
|
+
)
|
|
252
|
+
== i * 3
|
|
253
|
+
)
|
|
132
254
|
duration = time.time() - start
|
|
133
|
-
assert
|
|
255
|
+
assert (
|
|
256
|
+
duration < 3
|
|
257
|
+
), "There is a 2 second sleep in 'test_cached_property' but it should only run once."
|
|
258
|
+
|
|
134
259
|
|
|
135
260
|
@classes
|
|
136
261
|
@increment
|
|
137
262
|
@pytest.mark.asyncio_cooperative
|
|
138
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
|
+
"""
|
|
139
276
|
async_instance = cls(i, sync=False)
|
|
140
277
|
start = time.time()
|
|
141
278
|
assert await async_instance.test_cached_property == i * 3
|
|
@@ -146,7 +283,7 @@ async def test_cached_property_async(cls: type, i: int):
|
|
|
146
283
|
getter_coro = getter()
|
|
147
284
|
assert asyncio.iscoroutine(getter_coro), getter_coro
|
|
148
285
|
assert await getter_coro == i * 3
|
|
149
|
-
|
|
286
|
+
|
|
150
287
|
# Can we override them too?
|
|
151
288
|
with pytest.raises(SyncModeInAsyncContextError):
|
|
152
289
|
getter(sync=True)
|
|
@@ -156,16 +293,35 @@ async def test_cached_property_async(cls: type, i: int):
|
|
|
156
293
|
duration = time.time() - start
|
|
157
294
|
# For TestSync, the duration can be higher because the calls execute inside of a threadpool which limits the amount of concurrency.
|
|
158
295
|
target_duration = 5 if isinstance(async_instance, TestSync) else 2.1
|
|
159
|
-
assert
|
|
296
|
+
assert (
|
|
297
|
+
duration < target_duration
|
|
298
|
+
), "There is a 2 second sleep in 'test_cached_property' but it should only run once."
|
|
160
299
|
|
|
161
300
|
|
|
162
301
|
@pytest.mark.asyncio_cooperative
|
|
163
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
|
+
|
|
164
319
|
# Can the implementation work with an async context manager?
|
|
165
320
|
class AsyncContextManager(ASyncGenericBase):
|
|
166
321
|
async def __aenter__(self):
|
|
167
322
|
self.entered = True
|
|
168
323
|
return self
|
|
324
|
+
|
|
169
325
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
170
326
|
self.exited = True
|
|
171
327
|
|
|
@@ -173,13 +329,30 @@ async def test_asynchronous_context_manager():
|
|
|
173
329
|
assert cm.entered
|
|
174
330
|
assert cm.exited
|
|
175
331
|
|
|
332
|
+
|
|
176
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
|
+
"""
|
|
177
349
|
# Can the implementation work with a context manager?
|
|
178
350
|
|
|
179
351
|
class SyncContextManager(ASyncGenericBase):
|
|
180
352
|
def __enter__(self):
|
|
181
353
|
self.entered = True
|
|
182
354
|
return self
|
|
355
|
+
|
|
183
356
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
184
357
|
self.exited = True
|
|
185
358
|
|
|
@@ -187,33 +360,77 @@ def test_synchronous_context_manager():
|
|
|
187
360
|
assert cm.entered
|
|
188
361
|
assert cm.exited
|
|
189
362
|
|
|
363
|
+
|
|
190
364
|
@pytest.mark.asyncio_cooperative
|
|
191
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
|
+
|
|
192
384
|
# Does the implementation screw anything up with aiteration?
|
|
193
385
|
class ASyncObjectWithAiter(ASyncGenericBase):
|
|
194
386
|
def __init__(self):
|
|
195
387
|
self.count = 0
|
|
388
|
+
|
|
196
389
|
def __aiter__(self):
|
|
197
390
|
return self
|
|
391
|
+
|
|
198
392
|
async def __anext__(self):
|
|
199
393
|
if self.count < 3:
|
|
200
394
|
self.count += 1
|
|
201
395
|
return self.count
|
|
202
396
|
raise StopAsyncIteration
|
|
397
|
+
|
|
203
398
|
assert [item async for item in ASyncObjectWithAiter()] == [1, 2, 3]
|
|
204
399
|
|
|
400
|
+
|
|
205
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
|
+
|
|
206
420
|
# Does the implementation screw anything up with iteration?
|
|
207
421
|
class ASyncObjectWithIter(ASyncGenericBase):
|
|
208
422
|
def __init__(self):
|
|
209
423
|
self.count = 0
|
|
424
|
+
|
|
210
425
|
def __iter__(self):
|
|
211
426
|
return self
|
|
427
|
+
|
|
212
428
|
def __next__(self):
|
|
213
429
|
if self.count < 3:
|
|
214
430
|
self.count += 1
|
|
215
431
|
return self.count
|
|
216
432
|
raise StopIteration
|
|
433
|
+
|
|
217
434
|
assert list(ASyncObjectWithIter()) == [1, 2, 3]
|
|
218
435
|
|
|
219
436
|
|
|
@@ -223,13 +440,41 @@ class ClassWithGenFunc(ASyncGenericBase):
|
|
|
223
440
|
yield 1
|
|
224
441
|
yield 2
|
|
225
442
|
|
|
443
|
+
|
|
226
444
|
def test_bound_generator_meta_sync():
|
|
227
|
-
"""
|
|
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
|
+
"""
|
|
228
459
|
for _ in ClassWithGenFunc().generate():
|
|
229
460
|
assert isinstance(_, int)
|
|
230
461
|
|
|
462
|
+
|
|
231
463
|
@pytest.mark.asyncio_cooperative
|
|
232
464
|
async def test_bound_generator_meta_async():
|
|
233
|
-
"""
|
|
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
|
+
"""
|
|
234
479
|
async for _ in ClassWithGenFunc().generate():
|
|
235
|
-
assert isinstance(_, int)
|
|
480
|
+
assert isinstance(_, int)
|
tests/test_cache.py
CHANGED
|
@@ -8,63 +8,79 @@ from tests.fixtures import _test_kwargs
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def test_decorator_async_with_cache_type():
|
|
11
|
-
@a_sync.a_sync(default=
|
|
11
|
+
@a_sync.a_sync(default="async", cache_type="memory")
|
|
12
12
|
async def some_test_fn() -> int:
|
|
13
13
|
return 2
|
|
14
|
+
|
|
14
15
|
start = time()
|
|
15
16
|
assert asyncio.get_event_loop().run_until_complete(some_test_fn()) == 2
|
|
16
17
|
assert asyncio.get_event_loop().run_until_complete(some_test_fn()) == 2
|
|
17
18
|
duration = time() - start
|
|
18
|
-
assert
|
|
19
|
-
|
|
19
|
+
assert (
|
|
20
|
+
duration < 1.5
|
|
21
|
+
), "There is a 1 second sleep in this function but it should only run once."
|
|
22
|
+
_test_kwargs(some_test_fn, "async")
|
|
23
|
+
|
|
20
24
|
|
|
21
25
|
def test_decorator_async_with_cache_maxsize():
|
|
22
|
-
@a_sync.a_sync(default=
|
|
26
|
+
@a_sync.a_sync(default="async", ram_cache_maxsize=100)
|
|
23
27
|
def some_test_fn() -> int:
|
|
24
28
|
sleep(1)
|
|
25
29
|
return 2
|
|
30
|
+
|
|
26
31
|
start = time()
|
|
27
32
|
assert asyncio.get_event_loop().run_until_complete(some_test_fn()) == 2
|
|
28
33
|
assert asyncio.get_event_loop().run_until_complete(some_test_fn()) == 2
|
|
29
34
|
duration = time() - start
|
|
30
|
-
assert
|
|
31
|
-
|
|
35
|
+
assert (
|
|
36
|
+
duration < 1.5
|
|
37
|
+
), "There is a 1 second sleep in this function but it should only run once."
|
|
38
|
+
_test_kwargs(some_test_fn, "async")
|
|
39
|
+
|
|
32
40
|
|
|
33
41
|
# This will never succeed due to some task the ttl kwargs creates
|
|
34
42
|
def test_decorator_async_with_cache_ttl():
|
|
35
|
-
@a_sync.a_sync(default=
|
|
43
|
+
@a_sync.a_sync(default="async", cache_type="memory", ram_cache_ttl=5)
|
|
36
44
|
async def some_test_fn() -> int:
|
|
37
45
|
return 2
|
|
46
|
+
|
|
38
47
|
assert asyncio.get_event_loop().run_until_complete(some_test_fn()) == 2
|
|
39
|
-
_test_kwargs(some_test_fn,
|
|
48
|
+
_test_kwargs(some_test_fn, "async")
|
|
40
49
|
|
|
41
50
|
|
|
42
51
|
def test_decorator_sync_with_cache_type():
|
|
43
52
|
# Fails
|
|
44
|
-
@a_sync.a_sync(default=
|
|
53
|
+
@a_sync.a_sync(default="sync", cache_type="memory")
|
|
45
54
|
async def some_test_fn() -> int:
|
|
46
55
|
sleep(1)
|
|
47
56
|
return 2
|
|
57
|
+
|
|
48
58
|
start = time()
|
|
49
59
|
assert some_test_fn() == 2
|
|
50
60
|
assert some_test_fn() == 2
|
|
51
61
|
assert some_test_fn() == 2
|
|
52
62
|
duration = time() - start
|
|
53
|
-
assert
|
|
54
|
-
|
|
63
|
+
assert (
|
|
64
|
+
duration < 1.5
|
|
65
|
+
), "There is a 1 second sleep in this function but it should only run once."
|
|
66
|
+
_test_kwargs(some_test_fn, "sync")
|
|
55
67
|
|
|
56
|
-
|
|
68
|
+
|
|
69
|
+
@pytest.mark.skipif(True, reason="skipped manually")
|
|
57
70
|
def test_decorator_sync_with_cache_maxsize():
|
|
58
71
|
# Fails
|
|
59
72
|
# TODO diagnose and fix
|
|
60
|
-
@a_sync.a_sync(default="sync", cache_type=
|
|
73
|
+
@a_sync.a_sync(default="sync", cache_type="memory")
|
|
61
74
|
def some_test_fn() -> int:
|
|
62
75
|
sleep(1)
|
|
63
76
|
return 2
|
|
77
|
+
|
|
64
78
|
start = time()
|
|
65
79
|
assert some_test_fn() == 2
|
|
66
80
|
assert some_test_fn() == 2
|
|
67
81
|
assert some_test_fn() == 2
|
|
68
82
|
duration = time() - start
|
|
69
|
-
assert
|
|
70
|
-
|
|
83
|
+
assert (
|
|
84
|
+
duration < 1.5
|
|
85
|
+
), "There is a 1 second sleep in this function but it should only run once."
|
|
86
|
+
_test_kwargs(some_test_fn, "sync")
|