Skip to content

Commit eeb6402

Browse files
committed
Implement MypyItem.collect for pytest < 6.0
1 parent 4802444 commit eeb6402

2 files changed

Lines changed: 62 additions & 4 deletions

File tree

src/pytest_mypy.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ def __init__(self, *args, **kwargs):
145145
super().__init__(*args, **kwargs)
146146
self.add_marker(self.MARKER)
147147

148+
def collect(self):
149+
"""
150+
Partially work around https://github.com/pytest-dev/pytest/issues/8016
151+
for pytest < 6.0.
152+
"""
153+
yield self
154+
148155
@classmethod
149156
def from_parent(cls, *args, **kwargs):
150157
"""Override from_parent for compatibility."""

tests/test_pytest_mypy.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import signal
22
import textwrap
33

4+
import pexpect
45
import pytest
56

67

8+
PYTEST_VERSION = tuple(int(v) for v in pytest.__version__.split(".")[:2])
9+
10+
711
@pytest.fixture(
812
params=[
913
True, # xdist enabled, active
@@ -309,7 +313,8 @@ def pyfunc(x):
309313
assert result.ret != 0
310314

311315

312-
def test_looponfail(testdir):
316+
@pytest.mark.parametrize("module_name", ["__init__", "test_demo"])
317+
def test_looponfail(testdir, module_name):
313318
"""Ensure that the plugin works with --looponfail."""
314319

315320
pass_source = textwrap.dedent(
@@ -324,7 +329,7 @@ def pyfunc(x: int) -> str:
324329
return x * 2
325330
""",
326331
)
327-
pyfile = testdir.makepyfile(fail_source)
332+
pyfile = testdir.makepyfile(**{module_name: fail_source})
328333
looponfailroot = testdir.mkdir("looponfailroot")
329334
looponfailroot_pyfile = looponfailroot.join(pyfile.basename)
330335
pyfile.move(looponfailroot_pyfile)
@@ -345,6 +350,17 @@ def pyfunc(x: int) -> str:
345350
expect_timeout=30.0,
346351
)
347352

353+
num_tests = 2
354+
if module_name == "__init__" and (3, 10) <= PYTEST_VERSION < (6, 2):
355+
# https://github.com/pytest-dev/pytest/issues/8016
356+
# Pytest had a bug where it assumed only Packages would have a
357+
# basepath of __init__.py, but in this test, the MypyItems do too!
358+
# A MypyItem is not a Collector, so this originally led to an
359+
# AttributeError. As a workaround, MypyItem implements collect()
360+
# by yielding itself. That gets past the AttributeError, but
361+
# the bug takes a branch that only allows one Item to be collected.
362+
num_tests = 1
363+
348364
def _expect_session():
349365
child.expect("==== test session starts ====")
350366

@@ -356,7 +372,7 @@ def _expect_failure():
356372
# These only show with mypy>=0.730:
357373
# child.expect("==== mypy ====")
358374
# child.expect("Found 1 error in 1 file (checked 1 source file)")
359-
child.expect("2 failed")
375+
child.expect(str(num_tests) + " failed")
360376
child.expect("#### LOOPONFAILING ####")
361377
_expect_waiting()
362378

@@ -378,7 +394,18 @@ def _expect_success():
378394
# These only show with mypy>=0.730:
379395
# child.expect("==== mypy ====")
380396
# child.expect("Success: no issues found in 1 source file")
381-
child.expect("2 passed")
397+
try:
398+
child.expect(str(num_tests) + " passed")
399+
except pexpect.exceptions.TIMEOUT:
400+
if module_name == "__init__" and (6, 0) <= PYTEST_VERSION < (6, 2):
401+
# Mypy probably noticed the __init__.py problem during the
402+
# development of Pytest 6.0, but the error was addressed
403+
# with an isinstance assertion, breaking the MypyItem
404+
# workaround. Here, we hit that assertion:
405+
child.expect("AssertionError")
406+
child.expect("1 error")
407+
pytest.xfail("https://github.com/pytest-dev/pytest/issues/8016")
408+
raise
382409
_expect_waiting()
383410

384411
def _break():
@@ -391,3 +418,27 @@ def _break():
391418
_break()
392419
_fix()
393420
child.kill(signal.SIGTERM)
421+
422+
423+
def test_mypy_item_collect(testdir, xdist_args):
424+
"""Ensure coverage for a 3.10<=pytest<6.0 workaround."""
425+
testdir.makepyfile(
426+
"""
427+
def test_mypy_item_collect(request):
428+
plugin = request.config.pluginmanager.getplugin("mypy")
429+
mypy_items = [
430+
item
431+
for item in request.session.items
432+
if isinstance(item, plugin.MypyItem)
433+
]
434+
assert mypy_items
435+
for mypy_item in mypy_items:
436+
assert all(item is mypy_item for item in mypy_item.collect())
437+
""",
438+
)
439+
result = testdir.runpytest_subprocess("--mypy", *xdist_args)
440+
test_count = 1
441+
mypy_file_checks = 1
442+
mypy_status_check = 1
443+
result.assert_outcomes(passed=test_count + mypy_file_checks + mypy_status_check)
444+
assert result.ret == 0

0 commit comments

Comments
 (0)