Coverage for onionizer/tests/test_onionizer.py: 91%

189 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-04-06 11:39 +0200

1import contextlib 

2 

3import pytest as pytest 

4 

5import onionizer 

6 

7 

8@pytest.fixture 

9def func_that_adds(): 

10 def func(x: int, y: int) -> int: 

11 return x + y 

12 

13 return func 

14 

15 

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 

20 

21 def middleware2(x: int, y: int) -> onionizer.OnionGenerator[int]: 

22 result = yield (x, y + 1), {} 

23 return result 

24 

25 wrapped_func = onionizer.wrap(func_that_adds, [middleware1, middleware2]) 

26 result = wrapped_func(0, 0) 

27 

28 assert result == 3 

29 

30 

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 

35 

36 def middleware2(x: int, y: int) -> onionizer.OnionGenerator[int]: 

37 result = yield onionizer.UNCHANGED 

38 return result * 2 

39 

40 wrapped_func = onionizer.wrap(func_that_adds, [middleware1, middleware2]) 

41 result = wrapped_func(0, 0) 

42 assert result == 1 

43 

44 wrapped_func = onionizer.wrap(func_that_adds, [middleware2, middleware1]) 

45 result = wrapped_func(0, 0) 

46 assert result == 2 

47 

48 

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 

53 

54 def middleware2(x: int, y: int): 

55 result = yield onionizer.PositionalArgs(x, y + 1) 

56 return result 

57 

58 wrapped_func = onionizer.wrap(func_that_adds, [middleware1, middleware2]) 

59 result = wrapped_func(0, 0) 

60 assert result == 2 

61 

62 

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 

67 

68 def middleware2(x: int, y: int): 

69 result = yield onionizer.KeywordArgs({"x": x, "y": y + 1}) 

70 return result 

71 

72 wrapped_func = onionizer.wrap(func_that_adds, [middleware1, middleware2]) 

73 result = wrapped_func(x=0, y=0) 

74 assert result == 2 

75 

76 

77def test_preprocessor(func_that_adds): 

78 @onionizer.preprocessor 

79 def midd1(x: int, y: int): 

80 return onionizer.PositionalArgs(x + 1, y + 1) 

81 

82 assert midd1.__name__ == "midd1" 

83 

84 wrapped_func = onionizer.wrap(func_that_adds, [midd1]) 

85 result = wrapped_func(x=0, y=0) 

86 assert result == 2 

87 

88 

89def test_postprocessor(func_that_adds): 

90 @onionizer.postprocessor 

91 def midd1(val: int): 

92 return val ** 2 

93 

94 assert midd1.__name__ == "midd1" 

95 

96 wrapped_func = onionizer.wrap(func_that_adds, [midd1]) 

97 result = wrapped_func(x=1, y=1) 

98 assert result == 4 

99 

100 

101def test_postprocessor_with_multiple_values(): 

102 def dummy_func(x, y): 

103 return x, y 

104 

105 @onionizer.postprocessor 

106 def midd1(couple: tuple): 

107 c1, c2 = couple 

108 return c2, c1 

109 

110 wrapped_func = onionizer.wrap(dummy_func, [midd1]) 

111 result = wrapped_func(x=1, y=2) 

112 assert result == (2, 1) 

113 

114 

115def test_support_for_context_managers(): 

116 def func(x, y): 

117 return x / y 

118 

119 @onionizer.postprocessor 

120 def midd1(val): 

121 return val + 1 

122 

123 @contextlib.contextmanager 

124 def exception_catcher(): 

125 try: 

126 yield 

127 except Exception as e: 

128 raise RuntimeError("Exception caught") from e 

129 

130 wrapped_func = onionizer.wrap(func, [exception_catcher()]) 

131 with pytest.raises(RuntimeError) as e: 

132 wrapped_func(x=1, y=0) 

133 assert str(e.value) == "Exception caught" 

134 

135 another_wrapped_func = onionizer.wrap(func, [exception_catcher(), midd1]) 

136 assert another_wrapped_func(x=1, y=1) == 2 

137 

138 

139def test_decorator(): 

140 def middleware1(x, y): 

141 result = yield onionizer.KeywordArgs({"x": x + 1, "y": y}) 

142 return result 

143 

144 def middleware2(x, y): 

145 result = yield onionizer.KeywordArgs({"x": x, "y": y + 1}) 

146 return result 

147 

148 @onionizer.decorate([middleware1, middleware2]) 

149 def func(x, y): 

150 return x + y 

151 

152 @onionizer.decorate(middleware1) 

153 def func2(x, y): 

154 return x + y 

155 

156 result = func(x=0, y=0) 

157 assert result == 2 

158 

159 result2 = func2(x=0, y=0) 

160 assert result2 == 1 

161 

162 

163def test_incorrect_decorator(): 

164 with pytest.raises(TypeError) as e: 

165 

166 @onionizer.decorate(1) 

167 def func2(x, y): 

168 return x + y 

169 

170 assert ( 

171 str(e.value) == "middlewares must be a list of coroutines or a single coroutine" 

172 ) 

173 

174 

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 

180 

181 @onionizer.as_decorator 

182 def middleware2(x, y): 

183 result = yield onionizer.KeywordArgs({"x": x, "y": y + 1}) 

184 return result 

185 

186 @middleware1 

187 @middleware2 

188 def func(x, y): 

189 return x + y 

190 

191 result = func(x=0, y=0) 

192 assert result == 2 

193 

194 

195def test_uncompatible_signature(func_that_adds): 

196 def middleware1(*args): 

197 result = yield onionizer.UNCHANGED 

198 return result 

199 

200 with pytest.raises(ValueError): 

201 onionizer.wrap(func_that_adds, middlewares=[middleware1]) 

202 

203 

204def test_uncompatible_signature_but_disable_sigcheck(func_that_adds): 

205 def middleware1(*args): 

206 result = yield onionizer.UNCHANGED 

207 return result 

208 

209 onionizer.wrap(func_that_adds, middlewares=[middleware1], sigcheck=False) 

210 assert True 

211 

212 

213def test_unyielding_middleware(func_that_adds): 

214 def middleware1(*args): 

215 return "hello" 

216 

217 f2 = onionizer.wrap( 

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 ) 

226 

227 

228def test_tooyielding_middleware(func_that_adds): 

229 def middleware1(*args): 

230 yield onionizer.UNCHANGED 

231 yield onionizer.UNCHANGED 

232 

233 f2 = onionizer.wrap( 

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 ) 

242 

243 

244@pytest.mark.skip(reason="demo") 

245def test_incorrects_managers(func_that_adds): 

246 class MyManager: 

247 def __enter__(self): 

248 return self 

249 

250 f = onionizer.wrap(func_that_adds, middlewares=[MyManager()], sigcheck=False) 

251 with pytest.raises(TypeError): 

252 f(1, 2) 

253 f2 = onionizer.wrap( 

254 func_that_adds, middlewares=[MyManager()], sigcheck=False 

255 ) 

256 with pytest.raises(TypeError): 

257 f2(1, 2) 

258 

259 

260def test_incorrect_func(): 

261 with pytest.raises(TypeError) as e: 

262 onionizer.wrap(1, []) 

263 assert str(e.value) == "func must be callable" 

264 

265 

266def test_incorrect_midlist(func_that_adds): 

267 def middleware1(*args): 

268 result = yield onionizer.UNCHANGED 

269 return result 

270 

271 with pytest.raises(TypeError) as e: 

272 onionizer.wrap(func_that_adds, middlewares=middleware1) 

273 assert str(e.value) == "middlewares must be a list of coroutines" 

274 

275 

276def test_incorrect_yields(func_that_adds): 

277 def middleware1(x: int, y: int): 

278 yield 2 

279 return 1 

280 

281 with pytest.raises(TypeError) as e: 

282 onionizer.wrap(func_that_adds, middlewares=[middleware1])(1, 2) 

283 assert ( 

284 str(e.value) == "arguments must be a tuple of length 2, " 

285 "maybe use onionizer.PositionalArgs or onionizer.MixedArgs instead" 

286 )