Skip to content

Commit dbf24f0

Browse files
committed
update gitignore and add tests
1 parent 2d9e123 commit dbf24f0

File tree

8 files changed

+361
-2
lines changed

8 files changed

+361
-2
lines changed

.coveragerc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[report]
2+
exclude_also =
3+
# pragma: no cover
4+
if TYPE_CHECKING:
5+
raise NotImplementedError

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@
22
.vscode/
33

44
__pycache__/
5-
.venv
5+
.pytest_cache/
6+
.venv/
7+
src/*.egg-info/
8+
9+
.tox/

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ requires = ["setuptools", "setuptools-scm", "wheel"]
33
build-backend = "setuptools.build_meta"
44

55
[tool.setuptools_scm]
6-
write_to = "src/pyper/_version.py"
76

87
[project]
98
name = "python-pyper"
@@ -33,3 +32,6 @@ classifiers = [
3332
Documentation = "https://pyper-dev.github.io/pyper/"
3433
Repository = "https://github.com/pyper-dev/pyper"
3534
Issues = "https://github.com/pyper-dev/pyper/issues"
35+
36+
[tool.pytest.ini_options]
37+
asyncio_default_fixture_loop_scope = "function"

tests/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pytest>=8.3.3
2+
pytest_asyncio>=0.24
3+
tox>=4.23

tests/test_async.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from pyper import task
2+
import pytest
3+
4+
5+
def f1(data):
6+
return data
7+
8+
def f2(data):
9+
yield data
10+
11+
def f3(data):
12+
raise RuntimeError
13+
14+
async def af1(data):
15+
return data
16+
17+
async def af2(data):
18+
yield data
19+
20+
async def af3(data):
21+
raise RuntimeError
22+
23+
async def af4(data):
24+
async for row in data:
25+
yield row
26+
27+
async def consumer(data):
28+
total = 0
29+
async for i in data:
30+
total += i
31+
return total
32+
33+
@pytest.mark.asyncio
34+
async def test_pipeline():
35+
p = task(f1) >> task(f2)
36+
assert p(1).__next__() == 1
37+
38+
@pytest.mark.asyncio
39+
async def test_joined_pipeline():
40+
p = task(af1) >> task(af2) >> task(af4, join=True)
41+
assert await p(1).__anext__() == 1
42+
43+
@pytest.mark.asyncio
44+
async def test_consumer():
45+
p = task(af1) >> task(af2) & consumer
46+
assert await p(1) == 1
47+
48+
@pytest.mark.asyncio
49+
async def test_invalid_first_stage_concurrency():
50+
try:
51+
p = task(af1, concurrency=2) >> task(af2) & consumer
52+
await p(1)
53+
except Exception as e:
54+
assert isinstance(e, RuntimeError)
55+
else:
56+
raise AssertionError
57+
58+
@pytest.mark.asyncio
59+
async def test_invalid_first_stage_join():
60+
try:
61+
p = task(af1, join=True) >> task(af2) & consumer
62+
await p(1)
63+
except Exception as e:
64+
assert isinstance(e, RuntimeError)
65+
else:
66+
raise AssertionError
67+
68+
@pytest.mark.asyncio
69+
async def test_error_handling():
70+
try:
71+
p = task(af1) >> task(af2) >> task(af3) & consumer
72+
await p(1)
73+
except Exception as e:
74+
assert isinstance(e, RuntimeError)
75+
else:
76+
raise AssertionError
77+
78+
@pytest.mark.asyncio
79+
async def test_unified_pipeline():
80+
p = task(af1) >> task(f1) >> task(af2) >> task(f2) & consumer
81+
assert await p(1) == 1
82+
83+
@pytest.mark.asyncio
84+
async def test_error_handling_in_daemon():
85+
try:
86+
p = task(af1) >> task(af2) >> task(f3, daemon=True) & consumer
87+
await p(1)
88+
except Exception as e:
89+
assert isinstance(e, RuntimeError)
90+
else:
91+
raise AssertionError
92+

tests/test_sync.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import time
2+
from pyper import task
3+
4+
5+
def f1(data):
6+
return data
7+
8+
def f2(data):
9+
yield data
10+
11+
def f3(data):
12+
for row in data:
13+
yield row
14+
15+
def f4(a1, a2, a3, data, k1, k2):
16+
return data
17+
18+
def f5(data):
19+
# Make queue monitor timeout on main thread
20+
time.sleep(1.2)
21+
raise RuntimeError
22+
23+
def consumer(data):
24+
total = 0
25+
for i in data:
26+
total += i
27+
return total
28+
29+
def test_pipeline():
30+
p = task(f1) >> task(f2)
31+
assert p(1).__next__() == 1
32+
33+
def test_joined_pipeline():
34+
p = task(f1) >> task(f2) >> task(f3, join=True)
35+
assert p(1).__next__() == 1
36+
37+
def test_bind():
38+
p = task(f1) >> task(f4, bind=task.bind(1, 1, 1, k1=1, k2=2))
39+
assert p(1).__next__() == 1
40+
41+
def test_redundant_bind_ok():
42+
p = task(f1) >> task(f2, bind=task.bind())
43+
assert p(1).__next__() == 1
44+
45+
def test_consumer():
46+
p = task(f1) >> task(f2) & consumer
47+
assert p(1) == 1
48+
49+
def test_invalid_first_stage_concurrency():
50+
try:
51+
p = task(f1, concurrency=2) >> task(f2) & consumer
52+
p(1)
53+
except Exception as e:
54+
assert isinstance(e, RuntimeError)
55+
else:
56+
raise AssertionError
57+
58+
def test_invalid_first_stage_join():
59+
try:
60+
p = task(f1, join=True) >> task(f2) & consumer
61+
p(1)
62+
except Exception as e:
63+
assert isinstance(e, RuntimeError)
64+
else:
65+
raise AssertionError
66+
67+
def test_error_handling():
68+
try:
69+
p = task(f1) >> task(f2) >> task(f5) & consumer
70+
p(1)
71+
except Exception as e:
72+
print(e)
73+
assert isinstance(e, RuntimeError)
74+
else:
75+
raise AssertionError
76+
77+
def test_error_handling_in_daemon():
78+
try:
79+
p = task(f5, daemon=True) >> task(f2) & consumer
80+
p(1)
81+
except Exception as e:
82+
print(e)
83+
assert isinstance(e, RuntimeError)
84+
else:
85+
raise AssertionError

tests/test_task.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
from pyper import task, AsyncPipeline, Pipeline
2+
3+
4+
def func(x):
5+
return x
6+
7+
def gen(x):
8+
yield x
9+
10+
async def afunc(x):
11+
return x
12+
13+
async def agen(x):
14+
yield x
15+
16+
class Func:
17+
def __call__(self, x):
18+
return x
19+
20+
class Gen:
21+
def __call__(self, x):
22+
yield x
23+
24+
class AFunc:
25+
async def __call__(self, x):
26+
return x
27+
28+
class AGen:
29+
async def __call__(self, x):
30+
yield x
31+
32+
def test_as_decorator():
33+
p = task(func)
34+
assert isinstance(p, Pipeline)
35+
36+
def test_as_decorator_with_params():
37+
p = task(join=True, concurrency=2, throttle=2)(func)
38+
assert isinstance(p, Pipeline)
39+
40+
def test_as_wrapper_with_params():
41+
p = task(func, join=True, concurrency=2, throttle=2)
42+
assert isinstance(p, Pipeline)
43+
44+
def _try_invalid_concurrency(value, exc_type):
45+
try:
46+
task(func, concurrency=value)
47+
except Exception as e:
48+
return isinstance(e, exc_type)
49+
return False
50+
51+
def test_raise_for_invalid_concurrency():
52+
assert _try_invalid_concurrency(0, ValueError)
53+
assert _try_invalid_concurrency(-1, ValueError)
54+
assert _try_invalid_concurrency("1",TypeError)
55+
assert _try_invalid_concurrency(1.5, TypeError)
56+
57+
def _try_invalid_throttle(value, exc_type):
58+
try:
59+
task(func, throttle=value)
60+
except Exception as e:
61+
return isinstance(e, exc_type)
62+
return False
63+
64+
def test_raise_for_invalid_throttle():
65+
assert _try_invalid_throttle(-1, ValueError)
66+
assert _try_invalid_throttle("1",TypeError)
67+
assert _try_invalid_throttle(1.5, TypeError)
68+
69+
def test_raise_for_invalid_daemon():
70+
try:
71+
task(afunc, daemon=True)
72+
except Exception as e:
73+
assert isinstance(e, ValueError)
74+
else:
75+
raise AssertionError
76+
77+
def test_raise_for_invalid_func():
78+
try:
79+
task(1)
80+
except Exception as e:
81+
assert isinstance(e, TypeError)
82+
else:
83+
raise AssertionError
84+
85+
def test_async_task():
86+
p = task(afunc)
87+
assert isinstance(p, AsyncPipeline)
88+
89+
def test_piped_async_task():
90+
p = task(afunc) >> task(func)
91+
assert isinstance(p, AsyncPipeline)
92+
93+
def test_invalid_pipe():
94+
try:
95+
task(func) >> 1
96+
except Exception as e:
97+
assert isinstance(e, TypeError)
98+
else:
99+
raise AssertionError
100+
101+
def test_invalid_async_pipe():
102+
try:
103+
task(afunc) >> 1
104+
except Exception as e:
105+
assert isinstance(e, TypeError)
106+
else:
107+
raise AssertionError
108+
109+
def test_invalid_consumer():
110+
try:
111+
task(func) & 1
112+
except Exception as e:
113+
assert isinstance(e, TypeError)
114+
else:
115+
raise AssertionError
116+
117+
def test_invalid_async_consumer():
118+
try:
119+
task(afunc) & func
120+
except Exception as e:
121+
assert isinstance(e, TypeError)
122+
else:
123+
raise AssertionError
124+
125+
def test_gen_inspect():
126+
is_gen = lambda f: task(f).tasks[0].is_gen
127+
assert is_gen(gen)
128+
assert is_gen(agen)
129+
assert is_gen(Gen())
130+
assert is_gen(AGen())
131+
assert not is_gen(func)
132+
assert not is_gen(afunc)
133+
assert not is_gen(Func())
134+
assert not is_gen(AFunc())
135+
assert not is_gen(lambda x: x)
136+
137+
def test_async_inspect():
138+
is_async = lambda f: task(f).tasks[0].is_async
139+
assert is_async(afunc)
140+
assert is_async(agen)
141+
assert is_async(AFunc())
142+
assert is_async(AGen())
143+
assert not is_async(func)
144+
assert not is_async(Func())
145+
assert not is_async(gen)
146+
assert not is_async(Gen())
147+
assert not is_async(lambda x: x)
148+
149+
def test_repr():
150+
p = task(func)
151+
assert "Pipeline" in repr(p)

tox.ini

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[tox]
2+
envlist = py{3.12}
3+
4+
[testenv]
5+
deps =
6+
pytest
7+
pytest-asyncio
8+
pytest-cov
9+
commands =
10+
coverage erase
11+
pytest --cov={envsitepackagesdir}/pyper tests
12+
coverage report -m
13+
setenv =
14+
COVERAGE_FILE = .tox/.coverage
15+
16+
[coverage]
17+
rcfile = .coveragerc

0 commit comments

Comments
 (0)