Skip to content

Commit f84ec2e

Browse files
Add support for pytest 5.4
1 parent 4942c53 commit f84ec2e

File tree

6 files changed

+82
-35
lines changed

6 files changed

+82
-35
lines changed

pytest.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ addopts =
1010
doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL ELLIPSIS
1111
log_level = DEBUG
1212
junit_family = xunit2
13+
14+
# By default report warnings as errors
15+
filterwarnings =
16+
error

requirements.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ mypy==0.770
1212
pip-tools
1313
pip>=19.3
1414
pytest-cov
15-
pytest~=5.3.5
15+
pytest-html
16+
pytest~=5.4.1
1617
setuptools>=43
1718
tox-pyenv
1819
tox>=3.14.3

requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ pycodestyle==2.5.0 # via flake8
4343
pyflakes==2.1.1 # via flake8
4444
pyparsing==2.4.6 # via packaging
4545
pytest-cov==2.8.1 # via -r requirements.in
46-
pytest==5.3.5 # via -r requirements.in, pytest-cov
46+
pytest-html==2.1.1 # via -r requirements.in
47+
pytest-metadata==1.8.0 # via pytest-html
48+
pytest==5.4.1 # via -r requirements.in, pytest-cov, pytest-html, pytest-metadata
4749
python-debian==0.1.36 # via reuse
4850
pytoml==0.1.21 # via dflit, dflit-core
4951
regex==2020.2.20 # via black

src/pytest_mypy_testing/plugin.py

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
from .parser import MypyTestItem, parse_file
1717

1818

19+
PYTEST_VERSION = pytest.__version__
20+
PYTEST_VERISON_INFO = tuple(int(part) for part in PYTEST_VERSION.split("."))
21+
22+
1923
class MypyResult(NamedTuple):
2024
mypy_args: List[str]
2125
returncode: int
@@ -36,15 +40,27 @@ def __init__(
3640
self,
3741
name: str,
3842
parent: "PytestMypyFile",
39-
config: Config,
43+
*,
4044
mypy_item: MypyTestItem,
45+
config: Optional[Config] = None,
4146
) -> None:
42-
super().__init__(name, parent, config)
47+
if config is None:
48+
config = parent.config
49+
super().__init__(name, parent=parent, config=config)
4350
self.add_marker("mypy")
4451
self.mypy_item = mypy_item
4552
for mark in self.mypy_item.marks:
4653
self.add_marker(mark)
4754

55+
@classmethod
56+
def from_parent(cls, parent, name, mypy_item):
57+
if PYTEST_VERISON_INFO < (5, 4):
58+
return cls(
59+
parent=parent, name=name, config=parent.config, mypy_item=mypy_item
60+
)
61+
else:
62+
return super().from_parent(parent=parent, name=name, mypy_item=mypy_item)
63+
4864
def runtest(self) -> None:
4965
returncode, actual_messages = self.parent.run_mypy(self.mypy_item)
5066

@@ -59,44 +75,55 @@ def reportinfo(self) -> Tuple[str, Optional[int], str]:
5975
return self.parent.fspath, self.mypy_item.lineno, self.name
6076

6177
def repr_failure(self, excinfo, style=None):
62-
if excinfo.errisinstance(MypyAssertionError):
63-
exception_repr = excinfo.getrepr(style="short")
64-
exception_repr.reprcrash.message = ""
65-
exception_repr.reprtraceback.reprentries = [
66-
ReprEntry(
67-
filelocrepr=ReprFileLocation(
78+
if not excinfo.errisinstance(MypyAssertionError):
79+
return super().repr_failure(excinfo, style=style) # pragma: no cover
80+
reprfileloc_key = (
81+
"filelocrepr" if PYTEST_VERISON_INFO < (5, 4) else "reprfileloc"
82+
)
83+
exception_repr = excinfo.getrepr(style="short")
84+
exception_repr.reprcrash.message = ""
85+
exception_repr.reprtraceback.reprentries = [
86+
ReprEntry(
87+
lines=mismatch.lines,
88+
style="short",
89+
reprlocals=None,
90+
reprfuncargs=None,
91+
**{
92+
reprfileloc_key: ReprFileLocation(
6893
path=self.parent.fspath,
6994
lineno=mismatch.lineno,
7095
message=mismatch.error_message,
71-
),
72-
lines=mismatch.lines,
73-
style="short",
74-
reprlocals=None,
75-
reprfuncargs=None,
76-
)
77-
for mismatch in excinfo.value.errors
78-
]
79-
return exception_repr
80-
else:
81-
return super().repr_failure(excinfo, style=style) # pragma: no cover
96+
)
97+
},
98+
)
99+
for mismatch in excinfo.value.errors
100+
]
101+
return exception_repr
82102

83103

84104
class PytestMypyFile(pytest.File):
85105
def __init__(
86106
self, fspath: LocalPath, parent=None, config=None, session=None, nodeid=None,
87107
) -> None:
108+
if config is None:
109+
config = getattr(parent, "config", None)
88110
super().__init__(fspath, parent, config, session, nodeid)
89111
self.add_marker("mypy")
90112
self.mypy_file = parse_file(self.fspath)
91113
self._mypy_result: Optional[MypyResult] = None
92114

115+
@classmethod
116+
def from_parent(cls, parent, fspath):
117+
if PYTEST_VERISON_INFO < (5, 4):
118+
config = getattr(parent, "config", None)
119+
return cls(parent=parent, config=config, fspath=fspath)
120+
else:
121+
return super().from_parent(parent=parent, fspath=fspath)
122+
93123
def collect(self) -> Iterator[PytestMypyTestItem]:
94124
for item in self.mypy_file.items:
95-
yield PytestMypyTestItem(
96-
name="[mypy]" + item.name,
97-
parent=self,
98-
config=self.config,
99-
mypy_item=item,
125+
yield PytestMypyTestItem.from_parent(
126+
parent=self, name="[mypy]" + item.name, mypy_item=item,
100127
)
101128

102129
def run_mypy(self, item: MypyTestItem) -> Tuple[int, List[Message]]:
@@ -163,14 +190,17 @@ def pytest_collect_file(path: LocalPath, parent):
163190
if not hasattr(builtins, "reveal_type"):
164191
setattr(builtins, "reveal_type", lambda x: x)
165192

166-
config = getattr(parent, "config", None)
193+
if path.ext not in (".mypy-testing", ".py"):
194+
return None # pragma: no cover
195+
if not path.basename.startswith("test_"):
196+
return None # pragma: no cover
167197

168-
if path.ext in (".mypy-testing", ".py") and path.basename.startswith("test_"):
169-
file = PytestMypyFile(path, parent=parent, config=config)
170-
if file.mypy_file.items:
171-
return file
198+
file = PytestMypyFile.from_parent(parent=parent, fspath=path)
199+
200+
if file.mypy_file.items:
201+
return file
172202
else:
173-
return None # pragma: no cover
203+
return None
174204

175205

176206
def pytest_configure(config):

tests/test_plugin.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# SPDX-License-Identifier: CC0-1.0
33

44
from types import SimpleNamespace
5+
from unittest.mock import Mock
56

67
import pytest
78
from py._path.local import LocalPath
@@ -15,6 +16,8 @@
1516
)
1617
from pytest_mypy_testing.strutil import dedent
1718

19+
from _pytest.config import Config
20+
1821

1922
ERROR = Severity.ERROR
2023
NOTE = Severity.NOTE
@@ -30,7 +33,8 @@ def mk_dummy_parent(tmp_path, filename, content=""):
3033
path = tmp_path / filename
3134
path.write_text(content)
3235

33-
config = SimpleNamespace(rootdir=str(tmp_path))
36+
config = Mock(spec=Config)
37+
config.rootdir = str(tmp_path)
3438
session = SimpleNamespace(config=config, _initialpaths=[])
3539
parent = SimpleNamespace(
3640
config=config,

tox.ini

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,26 @@
33
[tox]
44
isolated_build = True
55
envlist =
6-
{py,py36,py37,py38}-pytest53-mypy{0750,0761,0770}
6+
{py,py36,py37,py38}-pytest{53,54}-mypy{0750,0761,0770}
77
linting
88

99
[testenv]
1010
deps =
1111
coverage[toml]
1212
dataclasses;python_version<"3.7"
1313
pytest53: pytest~=5.3.5
14+
pytest54: pytest~=5.4.1
1415
pytest-cov
16+
pytest-html
1517
mypy0750: mypy==0.750
1618
mypy0761: mypy==0.761
1719
mypy0770: mypy==0.770
20+
setenv =
21+
COVERAGE_FILE={toxinidir}/build/{envname}/coverage
1822
commands =
19-
coverage run -m pytest
23+
coverage run --context "{envname}" -m pytest \
24+
--html={toxinidir}/build/{envname}/pytest-report.html \
25+
--junitxml={toxinidir}/build/{envname}/junit.xml
2026

2127
[testenv:linting]
2228
deps =

0 commit comments

Comments
 (0)