|
1
|
|
|
from __future__ import print_function |
|
2
|
|
|
|
|
3
|
|
|
import inspect |
|
4
|
|
|
import os |
|
5
|
|
|
import subprocess |
|
6
|
|
|
import sys |
|
7
|
|
|
|
|
8
|
|
|
try: |
|
9
|
|
|
from StringIO import StringIO |
|
10
|
|
|
except ImportError: |
|
11
|
|
|
from io import StringIO |
|
12
|
|
|
try: |
|
13
|
|
|
from itertools import izip_longest |
|
14
|
|
|
except ImportError: |
|
15
|
|
|
from itertools import zip_longest as izip_longest |
|
16
|
|
|
|
|
17
|
|
|
import pytest |
|
18
|
|
|
|
|
19
|
|
|
from hunter import Q |
|
20
|
|
|
from hunter import And |
|
21
|
|
|
from hunter import Or |
|
22
|
|
|
from hunter import Query |
|
23
|
|
|
from hunter import When |
|
24
|
|
|
from hunter import stop |
|
25
|
|
|
from hunter import trace |
|
26
|
|
|
|
|
27
|
|
|
from hunter import CodePrinter |
|
28
|
|
|
from hunter import Debugger |
|
29
|
|
|
from hunter import VarsPrinter |
|
30
|
|
|
|
|
31
|
|
|
pytest_plugins = 'pytester', |
|
32
|
|
|
|
|
33
|
|
|
|
|
34
|
|
|
@pytest.yield_fixture(autouse=True, scope="function") |
|
35
|
|
|
def auto_stop(): |
|
36
|
|
|
try: |
|
37
|
|
|
yield |
|
38
|
|
|
finally: |
|
39
|
|
|
stop() |
|
40
|
|
|
|
|
41
|
|
|
|
|
42
|
|
|
def _get_func_spec(func): |
|
43
|
|
|
spec = inspect.getargspec(func) |
|
44
|
|
|
return inspect.formatargspec(spec.args, spec.varargs) |
|
45
|
|
|
|
|
46
|
|
|
|
|
47
|
|
|
def test_pth_activation(): |
|
48
|
|
|
module_name = os.path.__name__ |
|
49
|
|
|
expected_module = "{0}.py".format(module_name) |
|
50
|
|
|
hunter_env = "module={!r},function=\"join\"".format(module_name) |
|
51
|
|
|
func_spec = _get_func_spec(os.path.join) |
|
52
|
|
|
expected_call = "call def join{0}:".format(func_spec) |
|
53
|
|
|
|
|
54
|
|
|
output = subprocess.check_output( |
|
55
|
|
|
[sys.executable, os.path.join(os.path.dirname(__file__), 'sample.py')], |
|
56
|
|
|
env=dict(os.environ, PYTHONHUNTER=hunter_env), |
|
57
|
|
|
stderr=subprocess.STDOUT, |
|
58
|
|
|
) |
|
59
|
|
|
assert expected_module.encode() in output |
|
60
|
|
|
assert expected_call.encode() in output |
|
61
|
|
|
|
|
62
|
|
|
|
|
63
|
|
|
def test_pth_sample4(): |
|
64
|
|
|
env = dict(os.environ, PYTHONHUNTER="CodePrinter") |
|
65
|
|
|
env.pop('COVERAGE_PROCESS_START', None) |
|
66
|
|
|
env.pop('COV_CORE_SOURCE', None) |
|
67
|
|
|
output = subprocess.check_output( |
|
68
|
|
|
[sys.executable, os.path.join(os.path.dirname(__file__), 'sample4.py')], |
|
69
|
|
|
env=env, |
|
70
|
|
|
stderr=subprocess.STDOUT, |
|
71
|
|
|
) |
|
72
|
|
|
assert output |
|
73
|
|
|
|
|
74
|
|
|
|
|
75
|
|
|
def test_pth_sample2(LineMatcher): |
|
76
|
|
|
env = dict(os.environ, PYTHONHUNTER="module='__main__'") |
|
77
|
|
|
env.pop('COVERAGE_PROCESS_START', None) |
|
78
|
|
|
env.pop('COV_CORE_SOURCE', None) |
|
79
|
|
|
output = subprocess.check_output( |
|
80
|
|
|
[sys.executable, os.path.join(os.path.dirname(__file__), 'sample2.py')], |
|
81
|
|
|
env=env, |
|
82
|
|
|
stderr=subprocess.STDOUT, |
|
83
|
|
|
) |
|
84
|
|
|
lm = LineMatcher(output.decode('utf-8').splitlines()) |
|
85
|
|
|
lm.fnmatch_lines([ |
|
86
|
|
|
'*tests*sample2.py:* call if __name__ == "__main__": #*', |
|
87
|
|
|
'*tests*sample2.py:* line if __name__ == "__main__": #*', |
|
88
|
|
|
'*tests*sample2.py:* line import functools', |
|
89
|
|
|
'*tests*sample2.py:* line def deco(opt):', |
|
90
|
|
|
'*tests*sample2.py:* line @deco(1)', |
|
91
|
|
|
'*tests*sample2.py:* call def deco(opt):', |
|
92
|
|
|
'*tests*sample2.py:* line def decorator(func):', |
|
93
|
|
|
'*tests*sample2.py:* line return decorator', |
|
94
|
|
|
'*tests*sample2.py:* return return decorator', |
|
95
|
|
|
'* * ... return value: <function deco*', |
|
96
|
|
|
'*tests*sample2.py:* line @deco(2)', |
|
97
|
|
|
'*tests*sample2.py:* call def deco(opt):', |
|
98
|
|
|
'*tests*sample2.py:* line def decorator(func):', |
|
99
|
|
|
'*tests*sample2.py:* line return decorator', |
|
100
|
|
|
'*tests*sample2.py:* return return decorator', |
|
101
|
|
|
'* * ... return value: <function deco*', |
|
102
|
|
|
'*tests*sample2.py:* line @deco(3)', |
|
103
|
|
|
'*tests*sample2.py:* call def deco(opt):', |
|
104
|
|
|
'*tests*sample2.py:* line def decorator(func):', |
|
105
|
|
|
'*tests*sample2.py:* line return decorator', |
|
106
|
|
|
'*tests*sample2.py:* return return decorator', |
|
107
|
|
|
'* * ... return value: <function deco*', |
|
108
|
|
|
'*tests*sample2.py:* call def decorator(func):', |
|
109
|
|
|
'*tests*sample2.py:* line @functools.wraps(func)', |
|
110
|
|
|
'*tests*sample2.py:* line return wrapper', |
|
111
|
|
|
'*tests*sample2.py:* return return wrapper', |
|
112
|
|
|
'* * ... return value: <function foo *', |
|
113
|
|
|
'*tests*sample2.py:* call def decorator(func):', |
|
114
|
|
|
'*tests*sample2.py:* line @functools.wraps(func)', |
|
115
|
|
|
'*tests*sample2.py:* line return wrapper', |
|
116
|
|
|
'*tests*sample2.py:* return return wrapper', |
|
117
|
|
|
'* * ... return value: <function foo *', |
|
118
|
|
|
'*tests*sample2.py:* call def decorator(func):', |
|
119
|
|
|
'*tests*sample2.py:* line @functools.wraps(func)', |
|
120
|
|
|
'*tests*sample2.py:* line return wrapper', |
|
121
|
|
|
'*tests*sample2.py:* return return wrapper', |
|
122
|
|
|
'* * ... return value: <function foo *', |
|
123
|
|
|
'*tests*sample2.py:* line foo(', |
|
124
|
|
|
"*tests*sample2.py:* line 'a*',", |
|
125
|
|
|
"*tests*sample2.py:* line 'b'", |
|
126
|
|
|
'*tests*sample2.py:* call @functools.wraps(func)', |
|
127
|
|
|
'* * | def wrapper(*args):', |
|
128
|
|
|
'*tests*sample2.py:* line return func(*args)', |
|
129
|
|
|
'*tests*sample2.py:* call @functools.wraps(func)', |
|
130
|
|
|
'* * | def wrapper(*args):', |
|
131
|
|
|
'*tests*sample2.py:* line return func(*args)', |
|
132
|
|
|
'*tests*sample2.py:* call @functools.wraps(func)', |
|
133
|
|
|
'* * | def wrapper(*args):', |
|
134
|
|
|
'*tests*sample2.py:* line return func(*args)', |
|
135
|
|
|
'*tests*sample2.py:* call @deco(1)', |
|
136
|
|
|
'* * | @deco(2)', |
|
137
|
|
|
'* * | @deco(3)', |
|
138
|
|
|
'* * | def foo(*args):', |
|
139
|
|
|
'*tests*sample2.py:* line return args', |
|
140
|
|
|
'*tests*sample2.py:* return return args', |
|
141
|
|
|
"* * ... return value: ('a*', 'b')", |
|
142
|
|
|
"*tests*sample2.py:* return return func(*args)", |
|
143
|
|
|
"* * ... return value: ('a*', 'b')", |
|
144
|
|
|
"*tests*sample2.py:* return return func(*args)", |
|
145
|
|
|
"* * ... return value: ('a*', 'b')", |
|
146
|
|
|
"*tests*sample2.py:* return return func(*args)", |
|
147
|
|
|
"* * ... return value: ('a*', 'b')", |
|
148
|
|
|
"*tests*sample2.py:* line try:", |
|
149
|
|
|
"*tests*sample2.py:* line None(", |
|
150
|
|
|
"*tests*sample2.py:* line 'a',", |
|
151
|
|
|
"*tests*sample2.py:* line 'b'", |
|
152
|
|
|
"*tests*sample2.py:* exception 'b'", |
|
153
|
|
|
"* * ... exception value: *", |
|
154
|
|
|
"*tests*sample2.py:* line except:", |
|
155
|
|
|
"*tests*sample2.py:* line pass", |
|
156
|
|
|
"*tests*sample2.py:* return pass", |
|
157
|
|
|
"* ... return value: None", |
|
158
|
|
|
]) |
|
159
|
|
|
|
|
160
|
|
|
|
|
161
|
|
|
def test_repr(): |
|
162
|
|
|
assert repr(Q(module='a')) == "Query(module='a')" |
|
163
|
|
|
assert str(Q(module='a')) == "Query(module='a')" |
|
164
|
|
|
assert repr(Q(module='a', action='foo')) == "When(condition=Query(module='a'), actions=['foo'])" |
|
165
|
|
|
|
|
166
|
|
|
|
|
167
|
|
|
def test_nest_1(): |
|
168
|
|
|
assert repr(Q(Q(module='a'))) == "Query(module='a')" |
|
169
|
|
|
|
|
170
|
|
|
|
|
171
|
|
|
def test_expansion(): |
|
172
|
|
|
assert Q(1, 2, module=3) == Or(1, 2, Q(module=3)) |
|
173
|
|
|
assert Q(1, 2, module=3, action=4) == When(Or(1, 2, Q(module=3)), 4) |
|
174
|
|
|
assert Q(1, 2, module=3, actions=[4, 5]) == When(Or(1, 2, Q(module=3)), 4, 5) |
|
175
|
|
|
|
|
176
|
|
|
|
|
177
|
|
|
def test_and(): |
|
178
|
|
|
assert And(1, 2) == And(1, 2) |
|
179
|
|
|
assert Q(module=1) & Q(module=2) == And(Q(module=1), Q(module=2)) |
|
180
|
|
|
assert Q(module=1) & Q(module=2) & Q(module=3) == And(Q(module=1), Q(module=2), Q(module=3)) |
|
181
|
|
|
|
|
182
|
|
|
|
|
183
|
|
|
def test_or(): |
|
|
|
|
|
|
184
|
|
|
assert Q(module=1) | Q(module=2) == Or(Q(module=1), Q(module=2)) |
|
185
|
|
|
assert Q(module=1) | Q(module=2) | Q(module=3) == Or(Q(module=1), Q(module=2), Q(module=3)) |
|
186
|
|
|
|
|
187
|
|
|
|
|
188
|
|
|
def test_or(): |
|
|
|
|
|
|
189
|
|
|
assert Q(module=1) | Q(module=2) == Or(Q(module=1), Q(module=2)) |
|
190
|
|
|
assert Q(module=1) | Q(module=2) | Q(module=3) == Or(Q(module=1), Q(module=2), Q(module=3)) |
|
191
|
|
|
|
|
192
|
|
|
|
|
193
|
|
|
def test_tracing_bare(LineMatcher): |
|
194
|
|
|
lines = StringIO() |
|
195
|
|
|
with trace(CodePrinter(stream=lines)): |
|
196
|
|
|
def a(): |
|
197
|
|
|
return 1 |
|
198
|
|
|
|
|
199
|
|
|
b = a() |
|
200
|
|
|
b = 2 |
|
201
|
|
|
try: |
|
202
|
|
|
raise Exception("BOOM!") |
|
203
|
|
|
except Exception: |
|
204
|
|
|
pass |
|
205
|
|
|
print(lines.getvalue()) |
|
206
|
|
|
lm = LineMatcher(lines.getvalue().splitlines()) |
|
207
|
|
|
lm.fnmatch_lines([ |
|
208
|
|
|
"* ... return value: <hunter.*tracer.Tracer *", |
|
209
|
|
|
"*test_hunter.py* call def a():", |
|
210
|
|
|
"*test_hunter.py* line return 1", |
|
211
|
|
|
"*test_hunter.py* return return 1", |
|
212
|
|
|
"* ... return value: 1", |
|
213
|
|
|
]) |
|
214
|
|
|
|
|
215
|
|
|
|
|
216
|
|
|
def test_tracing_printing_failures(LineMatcher): |
|
217
|
|
|
lines = StringIO() |
|
218
|
|
|
with trace(CodePrinter(stream=lines), VarsPrinter("x", stream=lines)): |
|
219
|
|
|
class Bad(Exception): |
|
220
|
|
|
def __repr__(self): |
|
221
|
|
|
raise RuntimeError("I'm a bad class!") |
|
222
|
|
|
|
|
223
|
|
|
def a(): |
|
224
|
|
|
x = Bad() |
|
225
|
|
|
return x |
|
226
|
|
|
|
|
227
|
|
|
def b(): |
|
228
|
|
|
x = Bad() |
|
229
|
|
|
raise x |
|
230
|
|
|
|
|
231
|
|
|
a() |
|
232
|
|
|
try: |
|
233
|
|
|
b() |
|
234
|
|
|
except Exception as exc: |
|
235
|
|
|
pass |
|
236
|
|
|
lm = LineMatcher(lines.getvalue().splitlines()) |
|
237
|
|
|
lm.fnmatch_lines([ |
|
238
|
|
|
"""* ... return value: <hunter.*tracer.Tracer *""", |
|
239
|
|
|
"""*tests*test_hunter.py:* call class Bad(Exception):""", |
|
240
|
|
|
"""*tests*test_hunter.py:* line class Bad(Exception):""", |
|
241
|
|
|
"""*tests*test_hunter.py:* line def __repr__(self):""", |
|
242
|
|
|
"""*tests*test_hunter.py:* return def __repr__(self):""", |
|
243
|
|
|
"""* ... return value: *""", |
|
244
|
|
|
"""*tests*test_hunter.py:* call def a():""", |
|
245
|
|
|
"""*tests*test_hunter.py:* line x = Bad()""", |
|
246
|
|
|
"""*tests*test_hunter.py:* line return x""", |
|
247
|
|
|
"""* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""", |
|
248
|
|
|
"""*tests*test_hunter.py:* return return x""", |
|
249
|
|
|
"""* ... return value: !!! FAILED REPR: RuntimeError("I'm a bad class!",)""", |
|
250
|
|
|
"""* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""", |
|
251
|
|
|
"""*tests*test_hunter.py:* call def b():""", |
|
252
|
|
|
"""*tests*test_hunter.py:* line x = Bad()""", |
|
253
|
|
|
"""*tests*test_hunter.py:* line raise x""", |
|
254
|
|
|
"""* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""", |
|
255
|
|
|
"""*tests*test_hunter.py:* exception raise x""", |
|
256
|
|
|
"""* ... exception value: !!! FAILED REPR: RuntimeError("I'm a bad class!",)""", |
|
257
|
|
|
"""* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""", |
|
258
|
|
|
"""*tests*test_hunter.py:* return raise x""", |
|
259
|
|
|
"""* ... return value: None""", |
|
260
|
|
|
"""* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""", |
|
261
|
|
|
]) |
|
262
|
|
|
|
|
263
|
|
|
|
|
264
|
|
|
def test_tracing_vars(LineMatcher): |
|
265
|
|
|
lines = StringIO() |
|
266
|
|
|
with trace(actions=[VarsPrinter('b', stream=lines), CodePrinter(stream=lines)]): |
|
267
|
|
|
def a(): |
|
268
|
|
|
b = 1 |
|
269
|
|
|
b = 2 |
|
270
|
|
|
return 1 |
|
271
|
|
|
|
|
272
|
|
|
b = a() |
|
273
|
|
|
b = 2 |
|
274
|
|
|
try: |
|
275
|
|
|
raise Exception("BOOM!") |
|
276
|
|
|
except Exception: |
|
277
|
|
|
pass |
|
278
|
|
|
print(lines.getvalue()) |
|
279
|
|
|
lm = LineMatcher(lines.getvalue().splitlines()) |
|
280
|
|
|
lm.fnmatch_lines([ |
|
281
|
|
|
"* ... return value: <hunter.*tracer.Tracer *", |
|
282
|
|
|
"*test_hunter.py* call def a():", |
|
283
|
|
|
"*test_hunter.py* line b = 1", |
|
284
|
|
|
"* vars b => 1", |
|
285
|
|
|
"*test_hunter.py* line b = 2", |
|
286
|
|
|
"* vars b => 2", |
|
287
|
|
|
"*test_hunter.py* line return 1", |
|
288
|
|
|
"* vars b => 2", |
|
289
|
|
|
"*test_hunter.py* return return 1", |
|
290
|
|
|
"* ... return value: 1", |
|
291
|
|
|
]) |
|
292
|
|
|
|
|
293
|
|
|
|
|
294
|
|
|
def test_trace_merge(): |
|
295
|
|
|
trace(function="a") |
|
296
|
|
|
trace(function="b") |
|
297
|
|
|
assert trace(function="c")._handler == When(Q(function="c"), CodePrinter) |
|
298
|
|
|
|
|
299
|
|
|
|
|
300
|
|
|
def test_trace_api_expansion(): |
|
301
|
|
|
# simple use |
|
302
|
|
|
with trace(function="foobar") as t: |
|
303
|
|
|
assert t._handler == When(Q(function="foobar"), CodePrinter) |
|
304
|
|
|
|
|
305
|
|
|
# "or" by expression |
|
306
|
|
|
with trace(module="foo", function="foobar") as t: |
|
307
|
|
|
assert t._handler == When(Q(module="foo", function="foobar"), CodePrinter) |
|
308
|
|
|
|
|
309
|
|
|
# pdb.set_trace |
|
310
|
|
|
with trace(function="foobar", action=Debugger) as t: |
|
311
|
|
|
assert t._handler == When(Q(function="foobar"), Debugger) |
|
312
|
|
|
|
|
313
|
|
|
# pdb.set_trace on any hits |
|
314
|
|
|
with trace(module="foo", function="foobar", action=Debugger) as t: |
|
315
|
|
|
assert t._handler == When(Q(module="foo", function="foobar"), Debugger) |
|
316
|
|
|
|
|
317
|
|
|
# pdb.set_trace when function is foobar, otherwise just print when module is foo |
|
318
|
|
|
with trace(Q(function="foobar", action=Debugger), module="foo") as t: |
|
319
|
|
|
assert t._handler == When(Or( |
|
320
|
|
|
When(Q(function="foobar"), Debugger), |
|
321
|
|
|
Q(module="foo") |
|
322
|
|
|
), CodePrinter) |
|
323
|
|
|
|
|
324
|
|
|
# dumping variables from stack |
|
325
|
|
|
with trace(Q(function="foobar", action=VarsPrinter("foobar")), module="foo") as t: |
|
326
|
|
|
assert t._handler == When(Or( |
|
327
|
|
|
When(Q(function="foobar"), VarsPrinter("foobar")), |
|
328
|
|
|
Q(module="foo"), |
|
329
|
|
|
), CodePrinter) |
|
330
|
|
|
|
|
331
|
|
|
with trace(Q(function="foobar", action=VarsPrinter("foobar", "mumbojumbo")), module="foo") as t: |
|
332
|
|
|
assert t._handler == When(Or( |
|
333
|
|
|
When(Q(function="foobar"), VarsPrinter("foobar", "mumbojumbo")), |
|
334
|
|
|
Q(module="foo"), |
|
335
|
|
|
), CodePrinter) |
|
336
|
|
|
|
|
337
|
|
|
# multiple actions |
|
338
|
|
|
with trace(Q(function="foobar", actions=[VarsPrinter("foobar"), Debugger]), module="foo") as t: |
|
339
|
|
|
assert t._handler == When(Or( |
|
340
|
|
|
When(Q(function="foobar"), VarsPrinter("foobar"), Debugger), |
|
341
|
|
|
Q(module="foo"), |
|
342
|
|
|
), CodePrinter) |
|
343
|
|
|
|
|
344
|
|
|
# customization |
|
345
|
|
|
assert trace(lambda event: event.locals.get("node") == "Foobar", |
|
346
|
|
|
module="foo", function="foobar") |
|
347
|
|
|
assert trace(Q(lambda event: event.locals.get("node") == "Foobar", |
|
348
|
|
|
function="foobar", actions=[VarsPrinter("foobar"), Debugger]), module="foo", ) |
|
349
|
|
|
assert trace(Q(function="foobar", actions=[VarsPrinter("foobar"), |
|
350
|
|
|
lambda event: print("some custom output")]), module="foo", ) |
|
351
|
|
|
|
|
352
|
|
|
|
|
353
|
|
|
def test_trace_with_class_actions(): |
|
354
|
|
|
with trace(CodePrinter): |
|
355
|
|
|
def a(): |
|
356
|
|
|
pass |
|
357
|
|
|
|
|
358
|
|
|
a() |
|
359
|
|
|
|
|
360
|
|
|
|
|
361
|
|
|
def test_no_inf_recursion(): |
|
362
|
|
|
assert Or(And(1)) == 1 |
|
363
|
|
|
assert Or(Or(1)) == 1 |
|
364
|
|
|
assert And(Or(1)) == 1 |
|
365
|
|
|
assert And(And(1)) == 1 |
|
366
|
|
|
predicate = Q(Q(lambda ev: 1, module='wat')) |
|
367
|
|
|
print('predicate:', predicate) |
|
368
|
|
|
predicate('foo') |
|
369
|
|
|
|
|
370
|
|
|
|
|
371
|
|
|
def test_predicate_compression(): |
|
372
|
|
|
print(Or(Or(1, 2), And(3))) |
|
373
|
|
|
assert Or(Or(1, 2), And(3)) == Or(1, 2, 3) |
|
374
|
|
|
assert Or(Or(1, 2), 3) == Or(1, 2, 3) |
|
375
|
|
|
assert Or(1, Or(2, 3), 4) == Or(1, 2, 3, 4) |
|
376
|
|
|
assert And(1, 2, Or(3, 4)).predicates == (1, 2, Or(3, 4)) |
|
377
|
|
|
|
|
378
|
|
|
|
|
379
|
|
|
@pytest.mark.parametrize('expr,inp,expected', [ |
|
380
|
|
|
({'module': "abc"}, {'module': "abc"}, True), |
|
381
|
|
|
({'module': "abcd"}, {'module': "abc"}, False), |
|
382
|
|
|
({'module': "abcd"}, {'module': "abce"}, False), |
|
383
|
|
|
({'module': "abc"}, {'module': "abcd"}, False), |
|
384
|
|
|
({'module': ("abc", "xyz")}, {'module': "abc"}, True), |
|
385
|
|
|
({'module': ("abc", "xyz")}, {'module': "abcd"}, True), |
|
386
|
|
|
({'module': ("abc", "xyz")}, {'module': "xyzw"}, True), |
|
387
|
|
|
({'module': ("abc", "xyz")}, {'module': "fooabc"}, False), |
|
388
|
|
|
({'module': ("abc", "xyz")}, {'module': 1}, False), |
|
389
|
|
|
|
|
390
|
|
|
({'module': ["abc", "xyz"]}, {'module': "abc"}, True), |
|
391
|
|
|
({'module': ["abc", "xyz"]}, {'module': "abcd"}, True), |
|
392
|
|
|
({'module': ["abc", "xyz"]}, {'module': "xyzw"}, True), |
|
393
|
|
|
({'module': ["abc", "xyz"]}, {'module': "fooabc"}, False), |
|
394
|
|
|
({'module': ["abc", "xyz"]}, {'module': 1}, False), |
|
395
|
|
|
|
|
396
|
|
|
({'module': {"abc", "xyz"}}, {'module': "abc"}, True), |
|
397
|
|
|
({'module': {"abc", "xyz"}}, {'module': "abcd"}, True), |
|
398
|
|
|
({'module': {"abc", "xyz"}}, {'module': "xyzw"}, True), |
|
399
|
|
|
({'module': {"abc", "xyz"}}, {'module': "fooabc"}, False), |
|
400
|
|
|
({'module': {"abc", "xyz"}}, {'module': 1}, False), |
|
401
|
|
|
|
|
402
|
|
|
({'module': "abc"}, {'module': 1}, False), |
|
403
|
|
|
]) |
|
404
|
|
|
def test_matching(expr, inp, expected): |
|
405
|
|
|
assert Query(**expr)(inp) == expected |
|
406
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.