ez-a-sync 0.22.13__py3-none-any.whl → 0.22.15__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 +4 -3
- a_sync/__init__.py +30 -12
- a_sync/_smart.py +132 -28
- a_sync/_typing.py +56 -12
- a_sync/a_sync/__init__.py +35 -10
- a_sync/a_sync/_descriptor.py +74 -26
- a_sync/a_sync/_flags.py +14 -6
- a_sync/a_sync/_helpers.py +8 -7
- a_sync/a_sync/_kwargs.py +3 -2
- a_sync/a_sync/_meta.py +120 -28
- a_sync/a_sync/abstract.py +102 -28
- a_sync/a_sync/base.py +34 -16
- a_sync/a_sync/config.py +47 -13
- a_sync/a_sync/decorator.py +239 -117
- a_sync/a_sync/function.py +416 -146
- a_sync/a_sync/method.py +197 -59
- a_sync/a_sync/modifiers/__init__.py +47 -5
- a_sync/a_sync/modifiers/cache/__init__.py +46 -17
- a_sync/a_sync/modifiers/cache/memory.py +86 -20
- a_sync/a_sync/modifiers/limiter.py +52 -22
- a_sync/a_sync/modifiers/manager.py +98 -16
- a_sync/a_sync/modifiers/semaphores.py +48 -15
- a_sync/a_sync/property.py +383 -82
- a_sync/a_sync/singleton.py +1 -0
- a_sync/aliases.py +0 -1
- a_sync/asyncio/__init__.py +4 -1
- a_sync/asyncio/as_completed.py +177 -49
- a_sync/asyncio/create_task.py +31 -17
- a_sync/asyncio/gather.py +72 -52
- a_sync/asyncio/utils.py +3 -3
- a_sync/exceptions.py +78 -23
- a_sync/executor.py +120 -71
- a_sync/future.py +575 -158
- a_sync/iter.py +110 -50
- a_sync/primitives/__init__.py +14 -2
- a_sync/primitives/_debug.py +13 -13
- a_sync/primitives/_loggable.py +5 -4
- a_sync/primitives/locks/__init__.py +5 -2
- a_sync/primitives/locks/counter.py +38 -36
- a_sync/primitives/locks/event.py +21 -7
- a_sync/primitives/locks/prio_semaphore.py +182 -62
- a_sync/primitives/locks/semaphore.py +78 -77
- a_sync/primitives/queue.py +560 -58
- a_sync/sphinx/__init__.py +0 -1
- a_sync/sphinx/ext.py +160 -50
- a_sync/task.py +262 -97
- a_sync/utils/__init__.py +12 -6
- a_sync/utils/iterators.py +127 -43
- {ez_a_sync-0.22.13.dist-info → ez_a_sync-0.22.15.dist-info}/METADATA +1 -1
- ez_a_sync-0.22.15.dist-info/RECORD +74 -0
- {ez_a_sync-0.22.13.dist-info → ez_a_sync-0.22.15.dist-info}/WHEEL +1 -1
- tests/conftest.py +1 -2
- tests/executor.py +112 -9
- tests/fixtures.py +61 -32
- tests/test_abstract.py +7 -4
- tests/test_as_completed.py +54 -21
- tests/test_base.py +66 -17
- tests/test_cache.py +31 -15
- tests/test_decorator.py +54 -28
- tests/test_executor.py +8 -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 +15 -10
- tests/test_task.py +126 -28
- ez_a_sync-0.22.13.dist-info/RECORD +0 -74
- {ez_a_sync-0.22.13.dist-info → ez_a_sync-0.22.15.dist-info}/LICENSE.txt +0 -0
- {ez_a_sync-0.22.13.dist-info → ez_a_sync-0.22.15.dist-info}/top_level.txt +0 -0
tests/fixtures.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import asyncio
|
|
3
2
|
import time
|
|
4
3
|
from threading import current_thread, main_thread
|
|
@@ -11,97 +10,124 @@ from a_sync import ASyncBase
|
|
|
11
10
|
from a_sync.a_sync._meta import ASyncMeta, ASyncSingletonMeta
|
|
12
11
|
from a_sync.a_sync.singleton import ASyncGenericSingleton
|
|
13
12
|
|
|
14
|
-
increment = pytest.mark.parametrize(
|
|
13
|
+
increment = pytest.mark.parametrize("i", range(10))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WrongThreadError(Exception): ...
|
|
15
17
|
|
|
16
|
-
class WrongThreadError(Exception):
|
|
17
|
-
...
|
|
18
18
|
|
|
19
19
|
class TestClass(ASyncBase):
|
|
20
20
|
def __init__(self, v: int, sync: bool = False):
|
|
21
21
|
self.v = v
|
|
22
22
|
self.sync = sync
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
async def test_fn(self) -> int:
|
|
25
25
|
if self.sync == False and main_thread() != current_thread():
|
|
26
|
-
raise WrongThreadError(
|
|
26
|
+
raise WrongThreadError(
|
|
27
|
+
"This should be running on an event loop in the main thread."
|
|
28
|
+
)
|
|
27
29
|
elif self.sync == True and main_thread() != current_thread():
|
|
28
|
-
raise WrongThreadError(
|
|
30
|
+
raise WrongThreadError("This should be awaited in the main thread")
|
|
29
31
|
return self.v
|
|
30
|
-
|
|
32
|
+
|
|
31
33
|
@a_sync.aka.property
|
|
32
34
|
async def test_property(self) -> int:
|
|
33
35
|
if self.sync == False and main_thread() != current_thread():
|
|
34
|
-
raise WrongThreadError(
|
|
36
|
+
raise WrongThreadError(
|
|
37
|
+
"This should be running on an event loop in the main thread."
|
|
38
|
+
)
|
|
35
39
|
elif self.sync == True and main_thread() != current_thread():
|
|
36
|
-
raise WrongThreadError(
|
|
40
|
+
raise WrongThreadError("This should be awaited in the main thread")
|
|
37
41
|
return self.v * 2
|
|
38
|
-
|
|
42
|
+
|
|
39
43
|
@a_sync.alias.cached_property
|
|
40
44
|
async def test_cached_property(self) -> int:
|
|
41
45
|
if self.sync == False and main_thread() != current_thread():
|
|
42
|
-
raise WrongThreadError(
|
|
46
|
+
raise WrongThreadError(
|
|
47
|
+
"This should be running on an event loop in the main thread."
|
|
48
|
+
)
|
|
43
49
|
elif self.sync == True and main_thread() != current_thread():
|
|
44
|
-
raise WrongThreadError(
|
|
50
|
+
raise WrongThreadError("This should be awaited in the main thread")
|
|
45
51
|
await asyncio.sleep(2)
|
|
46
52
|
return self.v * 3
|
|
47
53
|
|
|
54
|
+
|
|
48
55
|
class TestSync(ASyncBase):
|
|
49
56
|
main = main_thread()
|
|
57
|
+
|
|
50
58
|
def __init__(self, v: int, sync: bool):
|
|
51
59
|
self.v = v
|
|
52
60
|
self.sync = sync
|
|
53
|
-
|
|
61
|
+
|
|
54
62
|
def test_fn(self) -> int:
|
|
55
63
|
# Sync bound methods are actually async functions that are run in an executor and awaited
|
|
56
64
|
if self.sync == False and main_thread() == current_thread():
|
|
57
|
-
raise WrongThreadError(
|
|
65
|
+
raise WrongThreadError(
|
|
66
|
+
"This should be running in an executor, not the main thread."
|
|
67
|
+
)
|
|
58
68
|
elif self.sync == True and main_thread() != current_thread():
|
|
59
|
-
raise WrongThreadError(
|
|
69
|
+
raise WrongThreadError(
|
|
70
|
+
"This should be running synchronously in the main thread"
|
|
71
|
+
)
|
|
60
72
|
return self.v
|
|
61
|
-
|
|
73
|
+
|
|
62
74
|
@a_sync.aka.property
|
|
63
75
|
def test_property(self) -> int:
|
|
64
76
|
if self.sync == False and main_thread() == current_thread():
|
|
65
|
-
raise WrongThreadError(
|
|
77
|
+
raise WrongThreadError(
|
|
78
|
+
"This should be running in an executor, not the main thread."
|
|
79
|
+
)
|
|
66
80
|
if self.sync == True and main_thread() == current_thread():
|
|
67
81
|
# Sync properties are actually async functions that are run in an executor and awaited
|
|
68
|
-
raise WrongThreadError(
|
|
82
|
+
raise WrongThreadError(
|
|
83
|
+
"This should be running in an executor, not the main thread."
|
|
84
|
+
)
|
|
69
85
|
return self.v * 2
|
|
70
|
-
|
|
86
|
+
|
|
71
87
|
@a_sync.alias.cached_property
|
|
72
88
|
def test_cached_property(self) -> int:
|
|
73
89
|
if self.sync == False and main_thread() == current_thread():
|
|
74
|
-
raise WrongThreadError(
|
|
90
|
+
raise WrongThreadError(
|
|
91
|
+
"This should be running in an executor, not the main thread."
|
|
92
|
+
)
|
|
75
93
|
if self.sync == True and main_thread() == current_thread():
|
|
76
94
|
# Sync properties are actually async functions that are run in an executor and awaited
|
|
77
|
-
raise WrongThreadError(
|
|
95
|
+
raise WrongThreadError(
|
|
96
|
+
"This should be running in an executor, not the main thread."
|
|
97
|
+
)
|
|
78
98
|
time.sleep(2)
|
|
79
99
|
return self.v * 3
|
|
80
100
|
|
|
101
|
+
|
|
81
102
|
class TestLimiter(TestClass):
|
|
82
103
|
limiter = 1
|
|
83
|
-
|
|
104
|
+
|
|
105
|
+
|
|
84
106
|
class TestInheritor(TestClass):
|
|
85
107
|
pass
|
|
86
108
|
|
|
109
|
+
|
|
87
110
|
class TestMeta(TestClass, metaclass=ASyncMeta):
|
|
88
111
|
pass
|
|
89
112
|
|
|
113
|
+
|
|
90
114
|
class TestSingleton(ASyncGenericSingleton, TestClass):
|
|
91
115
|
runs_per_minute = 100
|
|
92
116
|
pass
|
|
93
117
|
|
|
118
|
+
|
|
94
119
|
class TestSingletonMeta(TestClass, metaclass=ASyncSingletonMeta):
|
|
95
120
|
semaphore = 1
|
|
96
121
|
pass
|
|
97
122
|
|
|
123
|
+
|
|
98
124
|
class TestSemaphore(ASyncBase):
|
|
99
|
-
#semaphore=1 # NOTE: this is detected propely by undecorated test_fn but not the properties
|
|
100
|
-
|
|
125
|
+
# semaphore=1 # NOTE: this is detected propely by undecorated test_fn but not the properties
|
|
126
|
+
|
|
101
127
|
def __init__(self, v: int, sync: bool):
|
|
102
128
|
self.v = v
|
|
103
129
|
self.sync = sync
|
|
104
|
-
|
|
130
|
+
|
|
105
131
|
# spec on class and function both working
|
|
106
132
|
@a_sync.a_sync(semaphore=1)
|
|
107
133
|
async def test_fn(self) -> int:
|
|
@@ -109,11 +135,11 @@ class TestSemaphore(ASyncBase):
|
|
|
109
135
|
return self.v
|
|
110
136
|
|
|
111
137
|
# spec on class, function, property all working
|
|
112
|
-
@a_sync.aka.property(
|
|
138
|
+
@a_sync.aka.property("async", semaphore=1)
|
|
113
139
|
async def test_property(self) -> int:
|
|
114
140
|
await asyncio.sleep(1)
|
|
115
141
|
return self.v * 2
|
|
116
|
-
|
|
142
|
+
|
|
117
143
|
# spec on class, function, property all working
|
|
118
144
|
@a_sync.alias.cached_property(semaphore=50)
|
|
119
145
|
async def test_cached_property(self) -> int:
|
|
@@ -121,7 +147,7 @@ class TestSemaphore(ASyncBase):
|
|
|
121
147
|
return self.v * 3
|
|
122
148
|
|
|
123
149
|
|
|
124
|
-
def _test_kwargs(fn, default: Literal[
|
|
150
|
+
def _test_kwargs(fn, default: Literal["sync", "async", None]):
|
|
125
151
|
# force async
|
|
126
152
|
assert asyncio.get_event_loop().run_until_complete(fn(sync=False)) == 2
|
|
127
153
|
assert asyncio.get_event_loop().run_until_complete(fn(asynchronous=True)) == 2
|
|
@@ -132,18 +158,21 @@ def _test_kwargs(fn, default: Literal['sync','async',None]):
|
|
|
132
158
|
assert asyncio.get_event_loop().run_until_complete(fn(asynchronous=False)) == 2
|
|
133
159
|
assert fn(sync=True) == 2
|
|
134
160
|
assert fn(asynchronous=False) == 2
|
|
135
|
-
if default ==
|
|
161
|
+
if default == "sync":
|
|
136
162
|
assert fn() == 2
|
|
137
|
-
elif default ==
|
|
163
|
+
elif default == "async":
|
|
138
164
|
assert asyncio.get_event_loop().run_until_complete(fn()) == 2
|
|
139
165
|
|
|
166
|
+
|
|
140
167
|
async def sample_task(n):
|
|
141
168
|
await asyncio.sleep(0.01)
|
|
142
169
|
return n
|
|
143
170
|
|
|
171
|
+
|
|
144
172
|
async def timeout_task(n):
|
|
145
173
|
await asyncio.sleep(0.1)
|
|
146
174
|
return n
|
|
147
175
|
|
|
176
|
+
|
|
148
177
|
async def sample_exc(n):
|
|
149
|
-
raise ValueError("Sample error")
|
|
178
|
+
raise ValueError("Sample error")
|
tests/test_abstract.py
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
|
|
2
1
|
import sys
|
|
3
2
|
|
|
4
3
|
import pytest
|
|
5
4
|
|
|
6
5
|
from a_sync.a_sync.abstract import ASyncABC
|
|
7
6
|
|
|
8
|
-
_methods =
|
|
7
|
+
_methods = "__a_sync_default_mode__", "__a_sync_flag_name__", "__a_sync_flag_value__"
|
|
9
8
|
if sys.version_info >= (3, 12):
|
|
10
9
|
_MIDDLE = "without an implementation for abstract methods"
|
|
11
10
|
_methods = (f"'{method}'" for method in _methods)
|
|
12
11
|
else:
|
|
13
12
|
_MIDDLE = "with abstract methods"
|
|
14
13
|
|
|
14
|
+
|
|
15
15
|
def test_abc_direct_init():
|
|
16
|
-
with pytest.raises(
|
|
17
|
-
|
|
16
|
+
with pytest.raises(
|
|
17
|
+
TypeError,
|
|
18
|
+
match=f"Can't instantiate abstract class ASyncABC {_MIDDLE} {', '.join(_methods)}",
|
|
19
|
+
):
|
|
20
|
+
ASyncABC()
|
tests/test_as_completed.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import asyncio
|
|
3
2
|
|
|
4
3
|
import a_sync
|
|
@@ -11,7 +10,10 @@ from tests.fixtures import sample_exc, sample_task, timeout_task
|
|
|
11
10
|
async def test_as_completed_with_awaitables():
|
|
12
11
|
tasks = [sample_task(i) for i in range(5)]
|
|
13
12
|
results = [await result for result in a_sync.as_completed(tasks, aiter=False)]
|
|
14
|
-
assert sorted(results) == list(
|
|
13
|
+
assert sorted(results) == list(
|
|
14
|
+
range(5)
|
|
15
|
+
), "Results should be in ascending order from 0 to 4"
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
@pytest.mark.asyncio_cooperative
|
|
17
19
|
async def test_as_completed_with_awaitables_aiter():
|
|
@@ -19,42 +21,61 @@ async def test_as_completed_with_awaitables_aiter():
|
|
|
19
21
|
results = []
|
|
20
22
|
async for result in a_sync.as_completed(tasks, aiter=True):
|
|
21
23
|
results.append(result)
|
|
22
|
-
assert sorted(results) == list(
|
|
24
|
+
assert sorted(results) == list(
|
|
25
|
+
range(5)
|
|
26
|
+
), "Results should be in ascending order from 0 to 4"
|
|
27
|
+
|
|
23
28
|
|
|
24
29
|
@pytest.mark.asyncio_cooperative
|
|
25
30
|
async def test_as_completed_with_mapping():
|
|
26
|
-
tasks = {
|
|
31
|
+
tasks = {"task1": sample_task(1), "task2": sample_task(2)}
|
|
27
32
|
results = {}
|
|
28
33
|
for result in a_sync.as_completed(tasks, aiter=False):
|
|
29
34
|
key, value = await result
|
|
30
35
|
results[key] = value
|
|
31
|
-
assert results == {
|
|
36
|
+
assert results == {"task1": 1, "task2": 2}, "Results should match the input mapping"
|
|
37
|
+
|
|
32
38
|
|
|
33
39
|
@pytest.mark.asyncio_cooperative
|
|
34
40
|
async def test_as_completed_with_mapping_aiter():
|
|
35
|
-
tasks = {
|
|
41
|
+
tasks = {"task1": sample_task(1), "task2": sample_task(2)}
|
|
36
42
|
results = {}
|
|
37
43
|
async for key, result in a_sync.as_completed(tasks, aiter=True):
|
|
38
44
|
results[key] = result
|
|
39
|
-
assert results == {
|
|
45
|
+
assert results == {"task1": 1, "task2": 2}, "Results should match the input mapping"
|
|
46
|
+
|
|
40
47
|
|
|
41
48
|
@pytest.mark.asyncio_cooperative
|
|
42
49
|
async def test_as_completed_with_timeout():
|
|
43
50
|
tasks = [timeout_task(i) for i in range(2)]
|
|
44
51
|
with pytest.raises(asyncio.TimeoutError):
|
|
45
|
-
[
|
|
52
|
+
[
|
|
53
|
+
await result
|
|
54
|
+
for result in a_sync.as_completed(tasks, aiter=False, timeout=0.05)
|
|
55
|
+
]
|
|
56
|
+
|
|
46
57
|
|
|
47
58
|
@pytest.mark.asyncio_cooperative
|
|
48
59
|
async def test_as_completed_with_timeout_aiter():
|
|
49
60
|
tasks = [timeout_task(i) for i in range(2)]
|
|
50
61
|
with pytest.raises(asyncio.TimeoutError):
|
|
51
|
-
[
|
|
62
|
+
[
|
|
63
|
+
result
|
|
64
|
+
async for result in a_sync.as_completed(tasks, aiter=True, timeout=0.05)
|
|
65
|
+
]
|
|
66
|
+
|
|
52
67
|
|
|
53
68
|
@pytest.mark.asyncio_cooperative
|
|
54
69
|
async def test_as_completed_return_exceptions():
|
|
55
70
|
tasks = [sample_exc(i) for i in range(1)]
|
|
56
|
-
results = [
|
|
57
|
-
|
|
71
|
+
results = [
|
|
72
|
+
await result
|
|
73
|
+
for result in a_sync.as_completed(tasks, aiter=False, return_exceptions=True)
|
|
74
|
+
]
|
|
75
|
+
assert isinstance(
|
|
76
|
+
results[0], ValueError
|
|
77
|
+
), f"The result should be an exception {results}"
|
|
78
|
+
|
|
58
79
|
|
|
59
80
|
@pytest.mark.asyncio_cooperative
|
|
60
81
|
async def test_as_completed_return_exceptions_aiter():
|
|
@@ -64,11 +85,17 @@ async def test_as_completed_return_exceptions_aiter():
|
|
|
64
85
|
results.append(result)
|
|
65
86
|
assert isinstance(results[0], ValueError), "The result should be an exception"
|
|
66
87
|
|
|
88
|
+
|
|
67
89
|
@pytest.mark.asyncio_cooperative
|
|
68
90
|
async def test_as_completed_with_tqdm_disabled():
|
|
69
91
|
tasks = [sample_task(i) for i in range(5)]
|
|
70
|
-
results = [
|
|
71
|
-
|
|
92
|
+
results = [
|
|
93
|
+
await result for result in a_sync.as_completed(tasks, aiter=False, tqdm=False)
|
|
94
|
+
]
|
|
95
|
+
assert sorted(results) == list(
|
|
96
|
+
range(5)
|
|
97
|
+
), "Results should be in ascending order from 0 to 4"
|
|
98
|
+
|
|
72
99
|
|
|
73
100
|
@pytest.mark.asyncio_cooperative
|
|
74
101
|
async def test_as_completed_with_tqdm_disabled_aiter():
|
|
@@ -76,23 +103,29 @@ async def test_as_completed_with_tqdm_disabled_aiter():
|
|
|
76
103
|
results = []
|
|
77
104
|
async for result in a_sync.as_completed(tasks, aiter=True, tqdm=False):
|
|
78
105
|
results.append(result)
|
|
79
|
-
assert sorted(results) == list(
|
|
106
|
+
assert sorted(results) == list(
|
|
107
|
+
range(5)
|
|
108
|
+
), "Results should be in ascending order from 0 to 4"
|
|
109
|
+
|
|
80
110
|
|
|
81
111
|
@pytest.mark.asyncio_cooperative
|
|
82
112
|
async def test_as_completed_with_mapping_and_return_exceptions():
|
|
83
|
-
tasks = {
|
|
113
|
+
tasks = {"task1": sample_exc(1), "task2": sample_task(2)}
|
|
84
114
|
results = {}
|
|
85
115
|
for result in a_sync.as_completed(tasks, return_exceptions=True, aiter=False):
|
|
86
116
|
key, value = await result
|
|
87
117
|
results[key] = value
|
|
88
|
-
assert isinstance(results[
|
|
89
|
-
assert results[
|
|
118
|
+
assert isinstance(results["task1"], ValueError), "Result should be ValueError"
|
|
119
|
+
assert results["task2"] == 2, "Results should match the input mapping"
|
|
120
|
+
|
|
90
121
|
|
|
91
122
|
@pytest.mark.asyncio_cooperative
|
|
92
123
|
async def test_as_completed_with_mapping_and_return_exceptions_aiter():
|
|
93
|
-
tasks = {
|
|
124
|
+
tasks = {"task1": sample_exc(1), "task2": sample_task(2)}
|
|
94
125
|
results = {}
|
|
95
|
-
async for key, result in a_sync.as_completed(
|
|
126
|
+
async for key, result in a_sync.as_completed(
|
|
127
|
+
tasks, return_exceptions=True, aiter=True
|
|
128
|
+
):
|
|
96
129
|
results[key] = result
|
|
97
|
-
assert isinstance(results[
|
|
98
|
-
assert results[
|
|
130
|
+
assert isinstance(results["task1"], ValueError), "Result should be ValueError"
|
|
131
|
+
assert results["task2"] == 2, "Results should match the input mapping"
|
tests/test_base.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import asyncio
|
|
3
2
|
import time
|
|
4
3
|
|
|
@@ -9,7 +8,14 @@ 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():
|
|
@@ -17,9 +23,10 @@ def test_base_direct_init():
|
|
|
17
23
|
ASyncGenericBase()
|
|
18
24
|
|
|
19
25
|
|
|
20
|
-
classes = pytest.mark.parametrize(
|
|
26
|
+
classes = pytest.mark.parametrize("cls", [TestClass, TestSync, TestInheritor, TestMeta])
|
|
27
|
+
|
|
28
|
+
both_modes = pytest.mark.parametrize("sync", [True, False])
|
|
21
29
|
|
|
22
|
-
both_modes = pytest.mark.parametrize('sync', [True, False])
|
|
23
30
|
|
|
24
31
|
@classes
|
|
25
32
|
@both_modes
|
|
@@ -28,12 +35,13 @@ def test_inheritance(cls, sync: bool):
|
|
|
28
35
|
assert isinstance(instance, ASyncGenericBase)
|
|
29
36
|
assert isinstance(instance.__class__, ASyncMeta)
|
|
30
37
|
|
|
38
|
+
|
|
31
39
|
@classes
|
|
32
40
|
@increment
|
|
33
41
|
def test_method_sync(cls: type, i: int):
|
|
34
42
|
sync_instance = cls(i, sync=True)
|
|
35
43
|
assert sync_instance.test_fn() == i
|
|
36
|
-
|
|
44
|
+
|
|
37
45
|
# Can we override with kwargs?
|
|
38
46
|
assert sync_instance.test_fn(sync=True) == i
|
|
39
47
|
assert sync_instance.test_fn(asynchronous=False) == i
|
|
@@ -42,12 +50,27 @@ def test_method_sync(cls: type, i: int):
|
|
|
42
50
|
if isinstance(sync_instance, TestSync):
|
|
43
51
|
# this raises an assertion error inside of the test_fn execution. this is okay.
|
|
44
52
|
with pytest.raises(WrongThreadError):
|
|
45
|
-
asyncio.get_event_loop().run_until_complete(
|
|
53
|
+
asyncio.get_event_loop().run_until_complete(
|
|
54
|
+
sync_instance.test_fn(sync=False)
|
|
55
|
+
)
|
|
46
56
|
with pytest.raises(WrongThreadError):
|
|
47
|
-
asyncio.get_event_loop().run_until_complete(
|
|
57
|
+
asyncio.get_event_loop().run_until_complete(
|
|
58
|
+
sync_instance.test_fn(asynchronous=True)
|
|
59
|
+
)
|
|
48
60
|
else:
|
|
49
|
-
assert isinstance(
|
|
50
|
-
|
|
61
|
+
assert isinstance(
|
|
62
|
+
asyncio.get_event_loop().run_until_complete(
|
|
63
|
+
sync_instance.test_fn(sync=False)
|
|
64
|
+
),
|
|
65
|
+
int,
|
|
66
|
+
)
|
|
67
|
+
assert isinstance(
|
|
68
|
+
asyncio.get_event_loop().run_until_complete(
|
|
69
|
+
sync_instance.test_fn(asynchronous=True)
|
|
70
|
+
),
|
|
71
|
+
int,
|
|
72
|
+
)
|
|
73
|
+
|
|
51
74
|
|
|
52
75
|
@classes
|
|
53
76
|
@increment
|
|
@@ -58,7 +81,7 @@ async def test_method_async(cls: type, i: int):
|
|
|
58
81
|
# this raises an assertion error inside of the test_fn execution. this is okay.
|
|
59
82
|
with pytest.raises(WrongThreadError):
|
|
60
83
|
assert await async_instance.test_fn() == i
|
|
61
|
-
|
|
84
|
+
|
|
62
85
|
# Can we override with kwargs?
|
|
63
86
|
with pytest.raises(WrongThreadError):
|
|
64
87
|
async_instance.test_fn(sync=True)
|
|
@@ -84,7 +107,7 @@ async def test_method_async(cls: type, i: int):
|
|
|
84
107
|
def test_property_sync(cls: type, i: int):
|
|
85
108
|
sync_instance = cls(i, sync=True)
|
|
86
109
|
assert sync_instance.test_property == i * 2
|
|
87
|
-
|
|
110
|
+
|
|
88
111
|
# Can we access hidden methods for properties?
|
|
89
112
|
getter = sync_instance.__test_property__
|
|
90
113
|
assert isinstance(getter, HiddenMethod), getter
|
|
@@ -92,6 +115,7 @@ def test_property_sync(cls: type, i: int):
|
|
|
92
115
|
assert asyncio.iscoroutine(getter_coro), getter_coro
|
|
93
116
|
assert asyncio.get_event_loop().run_until_complete(getter_coro) == i * 2
|
|
94
117
|
|
|
118
|
+
|
|
95
119
|
@classes
|
|
96
120
|
@increment
|
|
97
121
|
@pytest.mark.asyncio_cooperative
|
|
@@ -117,7 +141,9 @@ def test_cached_property_sync(cls: type, i: int):
|
|
|
117
141
|
assert sync_instance.test_cached_property == i * 3
|
|
118
142
|
assert isinstance(sync_instance.test_cached_property, int)
|
|
119
143
|
duration = time.time() - start
|
|
120
|
-
assert
|
|
144
|
+
assert (
|
|
145
|
+
duration < 3
|
|
146
|
+
), "There is a 2 second sleep in 'test_cached_property' but it should only run once."
|
|
121
147
|
|
|
122
148
|
# Can we access hidden methods for properties?
|
|
123
149
|
start = time.time()
|
|
@@ -128,9 +154,17 @@ def test_cached_property_sync(cls: type, i: int):
|
|
|
128
154
|
assert asyncio.get_event_loop().run_until_complete(getter_coro) == i * 3
|
|
129
155
|
|
|
130
156
|
# Can we override them too?
|
|
131
|
-
assert
|
|
157
|
+
assert (
|
|
158
|
+
asyncio.get_event_loop().run_until_complete(
|
|
159
|
+
sync_instance.__test_cached_property__(sync=False)
|
|
160
|
+
)
|
|
161
|
+
== i * 3
|
|
162
|
+
)
|
|
132
163
|
duration = time.time() - start
|
|
133
|
-
assert
|
|
164
|
+
assert (
|
|
165
|
+
duration < 3
|
|
166
|
+
), "There is a 2 second sleep in 'test_cached_property' but it should only run once."
|
|
167
|
+
|
|
134
168
|
|
|
135
169
|
@classes
|
|
136
170
|
@increment
|
|
@@ -146,7 +180,7 @@ async def test_cached_property_async(cls: type, i: int):
|
|
|
146
180
|
getter_coro = getter()
|
|
147
181
|
assert asyncio.iscoroutine(getter_coro), getter_coro
|
|
148
182
|
assert await getter_coro == i * 3
|
|
149
|
-
|
|
183
|
+
|
|
150
184
|
# Can we override them too?
|
|
151
185
|
with pytest.raises(SyncModeInAsyncContextError):
|
|
152
186
|
getter(sync=True)
|
|
@@ -156,7 +190,9 @@ async def test_cached_property_async(cls: type, i: int):
|
|
|
156
190
|
duration = time.time() - start
|
|
157
191
|
# For TestSync, the duration can be higher because the calls execute inside of a threadpool which limits the amount of concurrency.
|
|
158
192
|
target_duration = 5 if isinstance(async_instance, TestSync) else 2.1
|
|
159
|
-
assert
|
|
193
|
+
assert (
|
|
194
|
+
duration < target_duration
|
|
195
|
+
), "There is a 2 second sleep in 'test_cached_property' but it should only run once."
|
|
160
196
|
|
|
161
197
|
|
|
162
198
|
@pytest.mark.asyncio_cooperative
|
|
@@ -166,6 +202,7 @@ async def test_asynchronous_context_manager():
|
|
|
166
202
|
async def __aenter__(self):
|
|
167
203
|
self.entered = True
|
|
168
204
|
return self
|
|
205
|
+
|
|
169
206
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
170
207
|
self.exited = True
|
|
171
208
|
|
|
@@ -173,6 +210,7 @@ async def test_asynchronous_context_manager():
|
|
|
173
210
|
assert cm.entered
|
|
174
211
|
assert cm.exited
|
|
175
212
|
|
|
213
|
+
|
|
176
214
|
def test_synchronous_context_manager():
|
|
177
215
|
# Can the implementation work with a context manager?
|
|
178
216
|
|
|
@@ -180,6 +218,7 @@ def test_synchronous_context_manager():
|
|
|
180
218
|
def __enter__(self):
|
|
181
219
|
self.entered = True
|
|
182
220
|
return self
|
|
221
|
+
|
|
183
222
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
184
223
|
self.exited = True
|
|
185
224
|
|
|
@@ -187,33 +226,41 @@ def test_synchronous_context_manager():
|
|
|
187
226
|
assert cm.entered
|
|
188
227
|
assert cm.exited
|
|
189
228
|
|
|
229
|
+
|
|
190
230
|
@pytest.mark.asyncio_cooperative
|
|
191
231
|
async def test_asynchronous_iteration():
|
|
192
232
|
# Does the implementation screw anything up with aiteration?
|
|
193
233
|
class ASyncObjectWithAiter(ASyncGenericBase):
|
|
194
234
|
def __init__(self):
|
|
195
235
|
self.count = 0
|
|
236
|
+
|
|
196
237
|
def __aiter__(self):
|
|
197
238
|
return self
|
|
239
|
+
|
|
198
240
|
async def __anext__(self):
|
|
199
241
|
if self.count < 3:
|
|
200
242
|
self.count += 1
|
|
201
243
|
return self.count
|
|
202
244
|
raise StopAsyncIteration
|
|
245
|
+
|
|
203
246
|
assert [item async for item in ASyncObjectWithAiter()] == [1, 2, 3]
|
|
204
247
|
|
|
248
|
+
|
|
205
249
|
def test_synchronous_iteration():
|
|
206
250
|
# Does the implementation screw anything up with iteration?
|
|
207
251
|
class ASyncObjectWithIter(ASyncGenericBase):
|
|
208
252
|
def __init__(self):
|
|
209
253
|
self.count = 0
|
|
254
|
+
|
|
210
255
|
def __iter__(self):
|
|
211
256
|
return self
|
|
257
|
+
|
|
212
258
|
def __next__(self):
|
|
213
259
|
if self.count < 3:
|
|
214
260
|
self.count += 1
|
|
215
261
|
return self.count
|
|
216
262
|
raise StopIteration
|
|
263
|
+
|
|
217
264
|
assert list(ASyncObjectWithIter()) == [1, 2, 3]
|
|
218
265
|
|
|
219
266
|
|
|
@@ -223,13 +270,15 @@ class ClassWithGenFunc(ASyncGenericBase):
|
|
|
223
270
|
yield 1
|
|
224
271
|
yield 2
|
|
225
272
|
|
|
273
|
+
|
|
226
274
|
def test_bound_generator_meta_sync():
|
|
227
275
|
"""Does the metaclass handle generator functions correctly?"""
|
|
228
276
|
for _ in ClassWithGenFunc().generate():
|
|
229
277
|
assert isinstance(_, int)
|
|
230
278
|
|
|
279
|
+
|
|
231
280
|
@pytest.mark.asyncio_cooperative
|
|
232
281
|
async def test_bound_generator_meta_async():
|
|
233
282
|
"""Does the metaclass handle generator functions correctly?"""
|
|
234
283
|
async for _ in ClassWithGenFunc().generate():
|
|
235
|
-
assert isinstance(_, int)
|
|
284
|
+
assert isinstance(_, int)
|