Skip to content

Commit 4802444

Browse files
authored
Merge pull request #118 from dmtucker/dev
Update dev deps and extend static checks
2 parents 4066a83 + 98429f7 commit 4802444

File tree

6 files changed

+270
-234
lines changed

6 files changed

+270
-234
lines changed

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
[build-system]
2-
requires = ['setuptools ~= 45.2.0', 'setuptools-scm ~= 3.5.0', 'wheel ~= 0.34.0']
2+
requires = ['setuptools ~= 50.3.0', 'setuptools-scm[toml] ~= 5.0.0', 'wheel ~= 0.36.0']
33
build-backend = 'setuptools.build_meta'
4+
5+
[tool.setuptools_scm]

setup.py

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,56 @@
44
import glob
55
import os
66
import codecs
7-
from setuptools import setup, find_packages
7+
from setuptools import setup, find_packages # type: ignore
88

99

1010
def read(fname):
1111
file_path = os.path.join(os.path.dirname(__file__), fname)
12-
return codecs.open(file_path, encoding='utf-8').read()
12+
return codecs.open(file_path, encoding="utf-8").read()
1313

1414

1515
setup(
16-
name='pytest-mypy',
16+
name="pytest-mypy",
1717
use_scm_version=True,
18-
author='Daniel Bader',
19-
author_email='[email protected]',
20-
maintainer='David Tucker',
21-
maintainer_email='[email protected]',
22-
license='MIT',
23-
url='https://github.com/dbader/pytest-mypy',
24-
description='Mypy static type checker plugin for Pytest',
25-
long_description=read('README.rst'),
26-
long_description_content_type='text/x-rst',
27-
packages=find_packages('src'),
28-
package_dir={'': 'src'},
18+
author="Daniel Bader",
19+
author_email="[email protected]",
20+
maintainer="David Tucker",
21+
maintainer_email="[email protected]",
22+
license="MIT",
23+
url="https://github.com/dbader/pytest-mypy",
24+
description="Mypy static type checker plugin for Pytest",
25+
long_description=read("README.rst"),
26+
long_description_content_type="text/x-rst",
27+
packages=find_packages("src"),
28+
package_dir={"": "src"},
2929
py_modules=[
30-
os.path.splitext(os.path.basename(path))[0]
31-
for path in glob.glob('src/*.py')
32-
],
33-
python_requires='>=3.5',
34-
setup_requires=[
35-
'setuptools-scm>=3.5',
30+
os.path.splitext(os.path.basename(path))[0] for path in glob.glob("src/*.py")
3631
],
32+
python_requires=">=3.5",
33+
setup_requires=["setuptools-scm>=3.5"],
3734
install_requires=[
38-
'attrs>=19.0',
39-
'filelock>=3.0',
40-
'pytest>=3.5',
35+
"attrs>=19.0",
36+
"filelock>=3.0",
37+
"pytest>=3.5",
4138
'mypy>=0.500; python_version<"3.8"',
4239
'mypy>=0.700; python_version>="3.8" and python_version<"3.9"',
4340
'mypy>=0.780; python_version>="3.9"',
4441
],
4542
classifiers=[
46-
'Development Status :: 4 - Beta',
47-
'Framework :: Pytest',
48-
'Intended Audience :: Developers',
49-
'Topic :: Software Development :: Testing',
50-
'Programming Language :: Python',
51-
'Programming Language :: Python :: 3',
52-
'Programming Language :: Python :: 3.5',
53-
'Programming Language :: Python :: 3.6',
54-
'Programming Language :: Python :: 3.7',
55-
'Programming Language :: Python :: 3.8',
56-
'Programming Language :: Python :: 3.9',
57-
'Programming Language :: Python :: Implementation :: CPython',
58-
'Operating System :: OS Independent',
59-
'License :: OSI Approved :: MIT License',
43+
"Development Status :: 4 - Beta",
44+
"Framework :: Pytest",
45+
"Intended Audience :: Developers",
46+
"Topic :: Software Development :: Testing",
47+
"Programming Language :: Python",
48+
"Programming Language :: Python :: 3",
49+
"Programming Language :: Python :: 3.5",
50+
"Programming Language :: Python :: 3.6",
51+
"Programming Language :: Python :: 3.7",
52+
"Programming Language :: Python :: 3.8",
53+
"Programming Language :: Python :: 3.9",
54+
"Programming Language :: Python :: Implementation :: CPython",
55+
"Operating System :: OS Independent",
56+
"License :: OSI Approved :: MIT License",
6057
],
61-
entry_points={
62-
'pytest11': [
63-
'mypy = pytest_mypy',
64-
],
65-
},
58+
entry_points={"pytest11": ["mypy = pytest_mypy"]},
6659
)

src/pytest_mypy.py

Lines changed: 51 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,32 @@
1212

1313

1414
mypy_argv = []
15-
nodeid_name = 'mypy'
15+
nodeid_name = "mypy"
1616

1717

1818
def default_file_error_formatter(item, results, errors):
1919
"""Create a string to be displayed when mypy finds errors in a file."""
20-
return '\n'.join(errors)
20+
return "\n".join(errors)
2121

2222

2323
file_error_formatter = default_file_error_formatter
2424

2525

2626
def pytest_addoption(parser):
2727
"""Add options for enabling and running mypy."""
28-
group = parser.getgroup('mypy')
28+
group = parser.getgroup("mypy")
29+
group.addoption("--mypy", action="store_true", help="run mypy on .py files")
2930
group.addoption(
30-
'--mypy', action='store_true',
31-
help='run mypy on .py files')
32-
group.addoption(
33-
'--mypy-ignore-missing-imports', action='store_true',
34-
help="suppresses error messages about imports that cannot be resolved")
31+
"--mypy-ignore-missing-imports",
32+
action="store_true",
33+
help="suppresses error messages about imports that cannot be resolved",
34+
)
3535

3636

3737
XDIST_WORKERINPUT_ATTRIBUTE_NAMES = (
38-
'workerinput',
38+
"workerinput",
3939
# xdist < 2.0.0:
40-
'slaveinput',
40+
"slaveinput",
4141
)
4242

4343

@@ -76,38 +76,38 @@ def pytest_configure(config):
7676

7777
# If xdist is enabled, then the results path should be exposed to
7878
# the workers so that they know where to read parsed results from.
79-
if config.pluginmanager.getplugin('xdist'):
79+
if config.pluginmanager.getplugin("xdist"):
80+
8081
class _MypyXdistPlugin:
8182
def pytest_configure_node(self, node): # xdist hook
8283
"""Pass config._mypy_results_path to workers."""
83-
_get_xdist_workerinput(node)['_mypy_results_path'] = \
84-
node.config._mypy_results_path
84+
_get_xdist_workerinput(node)[
85+
"_mypy_results_path"
86+
] = node.config._mypy_results_path
87+
8588
config.pluginmanager.register(_MypyXdistPlugin())
8689

8790
# pytest_terminal_summary cannot accept config before pytest 4.2.
8891
global _pytest_terminal_summary_config
8992
_pytest_terminal_summary_config = config
9093

9194
config.addinivalue_line(
92-
'markers',
93-
'{marker}: mark tests to be checked by mypy.'.format(
94-
marker=MypyItem.MARKER,
95-
),
95+
"markers",
96+
"{marker}: mark tests to be checked by mypy.".format(marker=MypyItem.MARKER),
9697
)
97-
if config.getoption('--mypy-ignore-missing-imports'):
98-
mypy_argv.append('--ignore-missing-imports')
98+
if config.getoption("--mypy-ignore-missing-imports"):
99+
mypy_argv.append("--ignore-missing-imports")
99100

100101

101102
def pytest_collect_file(path, parent):
102103
"""Create a MypyFileItem for every file mypy should run on."""
103-
if path.ext in {'.py', '.pyi'} and any([
104-
parent.config.option.mypy,
105-
parent.config.option.mypy_ignore_missing_imports,
106-
]):
104+
if path.ext in {".py", ".pyi"} and any(
105+
[parent.config.option.mypy, parent.config.option.mypy_ignore_missing_imports],
106+
):
107107
# Do not create MypyFile instance for a .py file if a
108108
# .pyi file with the same name already exists;
109109
# pytest will complain about duplicate modules otherwise
110-
if path.ext == '.pyi' or not path.new(ext='.pyi').isfile():
110+
if path.ext == ".pyi" or not path.new(ext=".pyi").isfile():
111111
return MypyFile.from_parent(parent=parent, fspath=path)
112112
return None
113113

@@ -120,17 +120,15 @@ class MypyFile(pytest.File):
120120
def from_parent(cls, *args, **kwargs):
121121
"""Override from_parent for compatibility."""
122122
# pytest.File.from_parent did not exist before pytest 5.4.
123-
return getattr(super(), 'from_parent', cls)(*args, **kwargs)
123+
return getattr(super(), "from_parent", cls)(*args, **kwargs)
124124

125125
def collect(self):
126126
"""Create a MypyFileItem for the File."""
127127
yield MypyFileItem.from_parent(parent=self, name=nodeid_name)
128128
# Since mypy might check files that were not collected,
129129
# pytest could pass even though mypy failed!
130130
# To prevent that, add an explicit check for the mypy exit status.
131-
if not any(
132-
isinstance(item, MypyStatusItem) for item in self.session.items
133-
):
131+
if not any(isinstance(item, MypyStatusItem) for item in self.session.items):
134132
yield MypyStatusItem.from_parent(
135133
parent=self,
136134
name=nodeid_name + "-status",
@@ -141,7 +139,7 @@ class MypyItem(pytest.Item):
141139

142140
"""A Mypy-related test Item."""
143141

144-
MARKER = 'mypy'
142+
MARKER = "mypy"
145143

146144
def __init__(self, *args, **kwargs):
147145
super().__init__(*args, **kwargs)
@@ -151,7 +149,7 @@ def __init__(self, *args, **kwargs):
151149
def from_parent(cls, *args, **kwargs):
152150
"""Override from_parent for compatibility."""
153151
# pytest.Item.from_parent did not exist before pytest 5.4.
154-
return getattr(super(), 'from_parent', cls)(*args, **kwargs)
152+
return getattr(super(), "from_parent", cls)(*args, **kwargs)
155153

156154
def repr_failure(self, excinfo):
157155
"""
@@ -193,7 +191,7 @@ def runtest(self):
193191
results = MypyResults.from_session(self.session)
194192
if results.status:
195193
raise MypyError(
196-
'mypy exited with status {status}.'.format(
194+
"mypy exited with status {status}.".format(
197195
status=results.status,
198196
),
199197
)
@@ -218,33 +216,32 @@ def dump(self, results_f: TextIO) -> None:
218216
return json.dump(vars(self), results_f)
219217

220218
@classmethod
221-
def load(cls, results_f: TextIO) -> 'MypyResults':
219+
def load(cls, results_f: TextIO) -> "MypyResults":
222220
"""Get results cached by dump()."""
223221
return cls(**json.load(results_f))
224222

225223
@classmethod
226224
def from_mypy(
227-
cls,
228-
items: List[MypyFileItem],
229-
*,
230-
opts: Optional[List[str]] = None
231-
) -> 'MypyResults':
225+
cls,
226+
items: List[MypyFileItem],
227+
*,
228+
opts: Optional[List[str]] = None # noqa: C816
229+
) -> "MypyResults":
232230
"""Generate results from mypy."""
233231

234232
if opts is None:
235233
opts = mypy_argv[:]
236234
abspath_errors = {
237-
os.path.abspath(str(item.fspath)): []
238-
for item in items
235+
os.path.abspath(str(item.fspath)): [] for item in items
239236
} # type: MypyResults._abspath_errors_type
240237

241238
stdout, stderr, status = mypy.api.run(opts + list(abspath_errors))
242239

243240
unmatched_lines = []
244-
for line in stdout.split('\n'):
241+
for line in stdout.split("\n"):
245242
if not line:
246243
continue
247-
path, _, error = line.partition(':')
244+
path, _, error = line.partition(":")
248245
abspath = os.path.abspath(path)
249246
try:
250247
abspath_errors[abspath].append(error)
@@ -257,27 +254,26 @@ def from_mypy(
257254
stderr=stderr,
258255
status=status,
259256
abspath_errors=abspath_errors,
260-
unmatched_stdout='\n'.join(unmatched_lines),
257+
unmatched_stdout="\n".join(unmatched_lines),
261258
)
262259

263260
@classmethod
264-
def from_session(cls, session) -> 'MypyResults':
261+
def from_session(cls, session) -> "MypyResults":
265262
"""Load (or generate) cached mypy results for a pytest session."""
266263
results_path = (
267264
session.config._mypy_results_path
268-
if _is_master(session.config) else
269-
_get_xdist_workerinput(session.config)['_mypy_results_path']
265+
if _is_master(session.config)
266+
else _get_xdist_workerinput(session.config)["_mypy_results_path"]
270267
)
271-
with FileLock(results_path + '.lock'):
268+
with FileLock(results_path + ".lock"):
272269
try:
273-
with open(results_path, mode='r') as results_f:
270+
with open(results_path, mode="r") as results_f:
274271
results = cls.load(results_f)
275272
except FileNotFoundError:
276-
results = cls.from_mypy([
277-
item for item in session.items
278-
if isinstance(item, MypyFileItem)
279-
])
280-
with open(results_path, mode='w') as results_f:
273+
results = cls.from_mypy(
274+
[item for item in session.items if isinstance(item, MypyFileItem)],
275+
)
276+
with open(results_path, mode="w") as results_f:
281277
results.dump(results_f)
282278
return results
283279

@@ -293,15 +289,15 @@ def pytest_terminal_summary(terminalreporter):
293289
"""Report stderr and unrecognized lines from stdout."""
294290
config = _pytest_terminal_summary_config
295291
try:
296-
with open(config._mypy_results_path, mode='r') as results_f:
292+
with open(config._mypy_results_path, mode="r") as results_f:
297293
results = MypyResults.load(results_f)
298294
except FileNotFoundError:
299295
# No MypyItems executed.
300296
return
301297
if results.unmatched_stdout or results.stderr:
302-
terminalreporter.section('mypy')
298+
terminalreporter.section("mypy")
303299
if results.unmatched_stdout:
304-
color = {'red': True} if results.status else {'green': True}
300+
color = {"red": True} if results.status else {"green": True}
305301
terminalreporter.write_line(results.unmatched_stdout, **color)
306302
if results.stderr:
307303
terminalreporter.write_line(results.stderr, yellow=True)

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
pytest_plugins = 'pytester'
1+
pytest_plugins = "pytester"

0 commit comments

Comments
 (0)