Coverage for onionizer/tests/test_onionizer.py: 95%
188 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-04-02 17:43 +0200
« prev ^ index » next coverage.py v7.2.2, created at 2023-04-02 17:43 +0200
1import contextlib
3import pytest as pytest
5import onionizer
8@pytest.fixture
9def func_that_adds():
10 def func(x: int, y: int) -> int:
11 return x + y
13 return func
16def test_mutate_arguments(func_that_adds):
17 def middleware1(x: int, y: int) -> onionizer.OnionGenerator[int]:
18 result = yield (x + 1, y + 1), {}
19 return result
21 def middleware2(x: int, y: int) -> onionizer.OnionGenerator[int]:
22 result = yield (x, y + 1), {}
23 return result
25 wrapped_func = onionizer.wrap_around(func_that_adds, [middleware1, middleware2])
26 result = wrapped_func(0, 0)
28 assert result == 3
31def test_mutate_output(func_that_adds):
32 def middleware1(x: int, y: int) -> onionizer.OnionGenerator[int]:
33 result = yield onionizer.UNCHANGED
34 return result + 1
36 def middleware2(x: int, y: int) -> onionizer.OnionGenerator[int]:
37 result = yield onionizer.UNCHANGED
38 return result * 2
40 wrapped_func = onionizer.wrap_around(func_that_adds, [middleware1, middleware2])
41 result = wrapped_func(0, 0)
42 assert result == 1
44 wrapped_func = onionizer.wrap_around(func_that_adds, [middleware2, middleware1])
45 result = wrapped_func(0, 0)
46 assert result == 2
49def test_pos_only(func_that_adds):
50 def middleware1(x: int, y: int):
51 result = yield onionizer.PositionalArgs(x + 1, y)
52 return result
54 def middleware2(x: int, y: int):
55 result = yield onionizer.PositionalArgs(x, y + 1)
56 return result
58 wrapped_func = onionizer.wrap_around(func_that_adds, [middleware1, middleware2])
59 result = wrapped_func(0, 0)
60 assert result == 2
63def test_kw_only(func_that_adds):
64 def middleware1(x: int, y: int):
65 result = yield onionizer.KeywordArgs({"x": x + 1, "y": y})
66 return result
68 def middleware2(x: int, y: int):
69 result = yield onionizer.KeywordArgs({"x": x, "y": y + 1})
70 return result
72 wrapped_func = onionizer.wrap_around(func_that_adds, [middleware1, middleware2])
73 result = wrapped_func(x=0, y=0)
74 assert result == 2
77def test_preprocessor(func_that_adds):
78 @onionizer.preprocessor
79 def midd1(x: int, y: int):
80 return onionizer.PositionalArgs(x + 1, y + 1)
82 assert midd1.__name__ == "midd1"
84 wrapped_func = onionizer.wrap_around(func_that_adds, [midd1])
85 result = wrapped_func(x=0, y=0)
86 assert result == 2
89def test_postprocessor(func_that_adds):
90 @onionizer.postprocessor
91 def midd1(val: int):
92 return val ** 2
94 assert midd1.__name__ == "midd1"
96 wrapped_func = onionizer.wrap_around(func_that_adds, [midd1])
97 result = wrapped_func(x=1, y=1)
98 assert result == 4
101def test_postprocessor_with_multiple_values():
102 def dummy_func(x, y):
103 return x, y
105 @onionizer.postprocessor
106 def midd1(couple: tuple):
107 c1, c2 = couple
108 return c2, c1
110 wrapped_func = onionizer.wrap_around(dummy_func, [midd1])
111 result = wrapped_func(x=1, y=2)
112 assert result == (2, 1)
115def test_support_for_context_managers():
116 def func(x, y):
117 return x / y
119 @onionizer.postprocessor
120 def midd1(val):
121 return val + 1
123 @contextlib.contextmanager
124 def exception_catcher():
125 try:
126 yield
127 except Exception as e:
128 raise RuntimeError("Exception caught") from e
130 wrapped_func = onionizer.wrap_around(func, [exception_catcher()])
131 with pytest.raises(RuntimeError) as e:
132 wrapped_func(x=1, y=0)
133 assert str(e.value) == "Exception caught"
135 another_wrapped_func = onionizer.wrap_around(func, [exception_catcher(), midd1])
136 assert another_wrapped_func(x=1, y=1) == 2
139def test_decorator():
140 def middleware1(x, y):
141 result = yield onionizer.KeywordArgs({"x": x + 1, "y": y})
142 return result
144 def middleware2(x, y):
145 result = yield onionizer.KeywordArgs({"x": x, "y": y + 1})
146 return result
148 @onionizer.decorate([middleware1, middleware2])
149 def func(x, y):
150 return x + y
152 @onionizer.decorate(middleware1)
153 def func2(x, y):
154 return x + y
156 result = func(x=0, y=0)
157 assert result == 2
159 result2 = func2(x=0, y=0)
160 assert result2 == 1
163def test_incorrect_decorator():
164 with pytest.raises(TypeError) as e:
166 @onionizer.decorate(1)
167 def func2(x, y):
168 return x + y
170 assert (
171 str(e.value) == "middlewares must be a list of coroutines or a single coroutine"
172 )
175def test_as_decorator():
176 @onionizer.as_decorator
177 def middleware1(x, y):
178 result = yield onionizer.KeywordArgs({"x": x + 1, "y": y})
179 return result
181 @onionizer.as_decorator
182 def middleware2(x, y):
183 result = yield onionizer.KeywordArgs({"x": x, "y": y + 1})
184 return result
186 @middleware1
187 @middleware2
188 def func(x, y):
189 return x + y
191 result = func(x=0, y=0)
192 assert result == 2
195def test_uncompatible_signature(func_that_adds):
196 def middleware1(*args):
197 result = yield onionizer.UNCHANGED
198 return result
200 with pytest.raises(ValueError):
201 onionizer.wrap_around(func_that_adds, middlewares=[middleware1])
204def test_uncompatible_signature_but_disable_sigcheck(func_that_adds):
205 def middleware1(*args):
206 result = yield onionizer.UNCHANGED
207 return result
209 onionizer.wrap_around(func_that_adds, middlewares=[middleware1], sigcheck=False)
210 assert True
213def test_unyielding_middleware(func_that_adds):
214 def middleware1(*args):
215 return "hello"
217 f2 = onionizer.wrap_around(
218 func_that_adds, middlewares=[middleware1], sigcheck=False
219 )
220 with pytest.raises(TypeError) as e:
221 f2(1, 2)
222 assert (
223 str(e.value) == "Middleware middleware1 is not a coroutine. "
224 "Did you forget to use a yield statement?"
225 )
228def test_tooyielding_middleware(func_that_adds):
229 def middleware1(*args):
230 yield onionizer.UNCHANGED
231 yield onionizer.UNCHANGED
233 f2 = onionizer.wrap_around(
234 func_that_adds, middlewares=[middleware1], sigcheck=False
235 )
236 with pytest.raises(RuntimeError) as e:
237 f2(1, 2)
238 assert (
239 str(e.value)
240 == "Generator did not exhaust. Your function should yield exactly once."
241 )
244def test_incorrects_managers(func_that_adds):
245 class MyManager:
246 def __enter__(self):
247 return self
249 f = onionizer.wrap_around(func_that_adds, middlewares=[MyManager()], sigcheck=False)
250 with pytest.raises(TypeError):
251 f(1, 2)
252 f2 = onionizer.wrap_around(
253 func_that_adds, middlewares=[MyManager()], sigcheck=False
254 )
255 with pytest.raises(TypeError):
256 f2(1, 2)
259def test_incorrect_func():
260 with pytest.raises(TypeError) as e:
261 onionizer.wrap_around(1, [])
262 assert str(e.value) == "func must be callable"
265def test_incorrect_midlist(func_that_adds):
266 def middleware1(*args):
267 result = yield onionizer.UNCHANGED
268 return result
270 with pytest.raises(TypeError) as e:
271 onionizer.wrap_around(func_that_adds, middlewares=middleware1)
272 assert str(e.value) == "middlewares must be a list of coroutines"
275def test_incorrect_yields(func_that_adds):
276 def middleware1(x: int, y: int):
277 yield 2
278 return 1
280 with pytest.raises(TypeError) as e:
281 onionizer.wrap_around(func_that_adds, middlewares=[middleware1])(1, 2)
282 assert (
283 str(e.value) == "arguments must be a tuple of length 2, "
284 "maybe use onionizer.PositionalArgs or onionizer.MixedArgs instead"
285 )