@@ -14,35 +14,41 @@ def xdist_args(request):
14
14
return ['-n' , 'auto' ] if request .param else []
15
15
16
16
17
- @pytest .mark .parametrize ('test_count ' , [1 , 2 ])
18
- def test_mypy_success (testdir , test_count , xdist_args ):
17
+ @pytest .mark .parametrize ('pyfile_count ' , [1 , 2 ])
18
+ def test_mypy_success (testdir , pyfile_count , xdist_args ):
19
19
"""Verify that running on a module with no type errors passes."""
20
20
testdir .makepyfile (
21
21
** {
22
- 'test_ ' + str (test_i ): '''
23
- def myfunc (x: int) -> int:
22
+ 'pyfile_ ' + str (pyfile_i ): '''
23
+ def pyfunc (x: int) -> int:
24
24
return x * 2
25
25
'''
26
- for test_i in range (test_count )
26
+ for pyfile_i in range (pyfile_count )
27
27
}
28
28
)
29
29
result = testdir .runpytest_subprocess (* xdist_args )
30
30
result .assert_outcomes ()
31
31
result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
32
- result .assert_outcomes (passed = test_count )
32
+ mypy_file_checks = pyfile_count
33
+ mypy_status_check = 1
34
+ mypy_checks = mypy_file_checks + mypy_status_check
35
+ result .assert_outcomes (passed = mypy_checks )
33
36
assert result .ret == 0
34
37
35
38
36
39
def test_mypy_error (testdir , xdist_args ):
37
40
"""Verify that running on a module with type errors fails."""
38
41
testdir .makepyfile ('''
39
- def myfunc (x: int) -> str:
42
+ def pyfunc (x: int) -> str:
40
43
return x * 2
41
44
''' )
42
45
result = testdir .runpytest_subprocess (* xdist_args )
43
46
result .assert_outcomes ()
44
47
result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
45
- result .assert_outcomes (failed = 1 )
48
+ mypy_file_checks = 1
49
+ mypy_status_check = 1
50
+ mypy_checks = mypy_file_checks + mypy_status_check
51
+ result .assert_outcomes (failed = mypy_checks )
46
52
result .stdout .fnmatch_lines ([
47
53
'2: error: Incompatible return value*' ,
48
54
])
@@ -54,20 +60,29 @@ def test_mypy_ignore_missings_imports(testdir, xdist_args):
54
60
Verify that --mypy-ignore-missing-imports
55
61
causes mypy to ignore missing imports.
56
62
"""
63
+ module_name = 'is_always_missing'
57
64
testdir .makepyfile ('''
58
- import pytest_mypy
59
- ''' )
65
+ try:
66
+ import {module_name}
67
+ except ImportError:
68
+ pass
69
+ ''' .format (module_name = module_name ))
60
70
result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
61
- result .assert_outcomes (failed = 1 )
71
+ mypy_file_checks = 1
72
+ mypy_status_check = 1
73
+ mypy_checks = mypy_file_checks + mypy_status_check
74
+ result .assert_outcomes (failed = mypy_checks )
62
75
result .stdout .fnmatch_lines ([
63
- "1: error: Cannot find *module named 'pytest_mypy'" ,
76
+ "2: error: Cannot find *module named '{module_name}'" .format (
77
+ module_name = module_name ,
78
+ ),
64
79
])
65
80
assert result .ret != 0
66
81
result = testdir .runpytest_subprocess (
67
82
'--mypy-ignore-missing-imports' ,
68
83
* xdist_args
69
84
)
70
- result .assert_outcomes (passed = 1 )
85
+ result .assert_outcomes (passed = mypy_checks )
71
86
assert result .ret == 0
72
87
73
88
@@ -78,28 +93,39 @@ def test_fails():
78
93
assert False
79
94
''' )
80
95
result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
81
- result .assert_outcomes (failed = 1 , passed = 1 )
96
+ test_count = 1
97
+ mypy_file_checks = 1
98
+ mypy_status_check = 1
99
+ mypy_checks = mypy_file_checks + mypy_status_check
100
+ result .assert_outcomes (failed = test_count , passed = mypy_checks )
82
101
assert result .ret != 0
83
102
result = testdir .runpytest_subprocess ('--mypy' , '-m' , 'mypy' , * xdist_args )
84
- result .assert_outcomes (passed = 1 )
103
+ result .assert_outcomes (passed = mypy_checks )
85
104
assert result .ret == 0
86
105
87
106
88
107
def test_non_mypy_error (testdir , xdist_args ):
89
108
"""Verify that non-MypyError exceptions are passed through the plugin."""
90
109
message = 'This is not a MypyError.'
91
- testdir .makepyfile ('''
92
- import pytest_mypy
110
+ testdir .makepyfile (conftest = '''
111
+ def pytest_configure(config):
112
+ plugin = config.pluginmanager.getplugin('mypy')
93
113
94
- def _patched_runtest(*args, **kwargs):
95
- raise Exception('{message}')
114
+ class PatchedMypyFileItem(plugin.MypyFileItem):
115
+ def runtest(self):
116
+ raise Exception('{message}')
96
117
97
- pytest_mypy.MypyItem.runtest = _patched_runtest
118
+ plugin.MypyFileItem = PatchedMypyFileItem
98
119
''' .format (message = message ))
99
120
result = testdir .runpytest_subprocess (* xdist_args )
100
121
result .assert_outcomes ()
101
122
result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
102
- result .assert_outcomes (failed = 1 )
123
+ mypy_file_checks = 1 # conftest.py
124
+ mypy_status_check = 1
125
+ result .assert_outcomes (
126
+ failed = mypy_file_checks , # patched to raise an Exception
127
+ passed = mypy_status_check , # conftest.py has no type errors.
128
+ )
103
129
result .stdout .fnmatch_lines (['*' + message ])
104
130
assert result .ret != 0
105
131
@@ -159,23 +185,75 @@ def pytest_configure(config):
159
185
160
186
161
187
def test_pytest_collection_modifyitems (testdir , xdist_args ):
188
+ """
189
+ Verify that collected files which are removed in a
190
+ pytest_collection_modifyitems implementation are not
191
+ checked by mypy.
192
+
193
+ This would also fail if a MypyStatusItem were injected
194
+ despite there being no MypyFileItems.
195
+ """
162
196
testdir .makepyfile (conftest = '''
163
197
def pytest_collection_modifyitems(session, config, items):
164
198
plugin = config.pluginmanager.getplugin('mypy')
165
199
for mypy_item_i in reversed([
166
200
i
167
201
for i, item in enumerate(items)
168
- if isinstance(item, plugin.MypyItem )
202
+ if isinstance(item, plugin.MypyFileItem )
169
203
]):
170
204
items.pop(mypy_item_i)
171
205
''' )
172
206
testdir .makepyfile ('''
173
- def myfunc (x: int) -> str:
207
+ def pyfunc (x: int) -> str:
174
208
return x * 2
175
209
176
210
def test_pass():
177
211
pass
178
212
''' )
179
213
result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
180
- result .assert_outcomes (passed = 1 )
214
+ test_count = 1
215
+ result .assert_outcomes (passed = test_count )
181
216
assert result .ret == 0
217
+
218
+
219
+ def test_mypy_indirect (testdir , xdist_args ):
220
+ """Verify that uncollected files checked by mypy cause a failure."""
221
+ testdir .makepyfile (bad = '''
222
+ def pyfunc(x: int) -> str:
223
+ return x * 2
224
+ ''' )
225
+ testdir .makepyfile (good = '''
226
+ import bad
227
+ ''' )
228
+ xdist_args .append ('good.py' ) # Nothing may come after xdist_args in py34.
229
+ result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
230
+ assert result .ret != 0
231
+
232
+
233
+ def test_mypy_indirect_inject (testdir , xdist_args ):
234
+ """
235
+ Verify that uncollected files checked by mypy because of a MypyFileItem
236
+ injected in pytest_collection_modifyitems cause a failure.
237
+ """
238
+ testdir .makepyfile (bad = '''
239
+ def pyfunc(x: int) -> str:
240
+ return x * 2
241
+ ''' )
242
+ testdir .makepyfile (good = '''
243
+ import bad
244
+ ''' )
245
+ testdir .makepyfile (conftest = '''
246
+ import py
247
+ import pytest
248
+
249
+ @pytest.hookimpl(trylast=True) # Inject as late as possible.
250
+ def pytest_collection_modifyitems(session, config, items):
251
+ plugin = config.pluginmanager.getplugin('mypy')
252
+ items.append(
253
+ plugin.MypyFileItem(py.path.local('good.py'), session),
254
+ )
255
+ ''' )
256
+ testdir .mkdir ('empty' )
257
+ xdist_args .append ('empty' ) # Nothing may come after xdist_args in py34.
258
+ result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
259
+ assert result .ret != 0
0 commit comments