7
7
from pathlib import Path
8
8
from tempfile import NamedTemporaryFile
9
9
import typing
10
- import warnings
11
10
12
11
from filelock import FileLock
13
12
import mypy .api
@@ -227,28 +226,34 @@ def repr_failure(
227
226
return super ().repr_failure (excinfo )
228
227
229
228
229
+ def _error_severity (error : str ) -> str :
230
+ components = [component .strip () for component in error .split (":" )]
231
+ # The second component is either the line or the severity:
232
+ # demo/note.py:2: note: By default the bodies of untyped functions are not checked
233
+ # demo/sub/conftest.py: error: Duplicate module named "conftest"
234
+ return components [2 ] if components [1 ].isdigit () else components [1 ]
235
+
236
+
230
237
class MypyFileItem (MypyItem ):
231
238
"""A check for Mypy errors in a File."""
232
239
233
240
def runtest (self ) -> None :
234
241
"""Raise an exception if mypy found errors for this item."""
235
242
results = MypyResults .from_session (self .session )
236
- abspath = str (self .path .absolute ())
237
- errors = results .abspath_errors .get (abspath )
238
- if errors :
239
- if not all (
240
- error .partition (":" )[2 ].partition (":" )[0 ].strip () == "note"
241
- for error in errors
242
- ):
243
- if self .session .config .option .mypy_xfail :
244
- self .add_marker (
245
- pytest .mark .xfail (
246
- raises = MypyError ,
247
- reason = "mypy errors are expected by --mypy-xfail." ,
248
- )
243
+ abspath = str (self .path .resolve ())
244
+ errors = [
245
+ error .partition (":" )[2 ].strip ()
246
+ for error in results .abspath_errors .get (abspath , [])
247
+ ]
248
+ if errors and not all (_error_severity (error ) == "note" for error in errors ):
249
+ if self .session .config .option .mypy_xfail :
250
+ self .add_marker (
251
+ pytest .mark .xfail (
252
+ raises = MypyError ,
253
+ reason = "mypy errors are expected by --mypy-xfail." ,
249
254
)
250
- raise MypyError ( file_error_formatter ( self , results , errors ) )
251
- warnings . warn ( " \n " + " \n " . join ( errors ), MypyWarning )
255
+ )
256
+ raise MypyError ( file_error_formatter ( self , results , errors ))
252
257
253
258
def reportinfo (self ) -> Tuple [str , None , str ]:
254
259
"""Produce a heading for the test report."""
@@ -312,7 +317,7 @@ def from_mypy(
312
317
if opts is None :
313
318
opts = mypy_argv [:]
314
319
abspath_errors = {
315
- str (path .absolute ()): [] for path in paths
320
+ str (path .resolve ()): [] for path in paths
316
321
} # type: MypyResults._abspath_errors_type
317
322
318
323
cwd = Path .cwd ()
@@ -325,9 +330,9 @@ def from_mypy(
325
330
if not line :
326
331
continue
327
332
path , _ , error = line .partition (":" )
328
- abspath = str (Path (path ).absolute ())
333
+ abspath = str (Path (path ).resolve ())
329
334
try :
330
- abspath_errors [abspath ].append (error )
335
+ abspath_errors [abspath ].append (line )
331
336
except KeyError :
332
337
unmatched_lines .append (line )
333
338
@@ -368,10 +373,6 @@ class MypyError(Exception):
368
373
"""
369
374
370
375
371
- class MypyWarning (pytest .PytestWarning ):
372
- """A non-failure message regarding the mypy run."""
373
-
374
-
375
376
class MypyControllerPlugin :
376
377
"""A plugin that is not registered on xdist worker processes."""
377
378
@@ -388,15 +389,25 @@ def pytest_terminal_summary(
388
389
except FileNotFoundError :
389
390
# No MypyItems executed.
390
391
return
391
- if config .option .mypy_xfail or results .unmatched_stdout or results .stderr :
392
- terminalreporter .section (terminal_summary_title )
392
+ if not results .stdout and not results .stderr :
393
+ return
394
+ terminalreporter .section (terminal_summary_title )
395
+ if results .stdout :
393
396
if config .option .mypy_xfail :
394
397
terminalreporter .write (results .stdout )
395
- elif results .unmatched_stdout :
396
- color = {"red" : True } if results .status else {"green" : True }
397
- terminalreporter .write_line (results .unmatched_stdout , ** color )
398
- if results .stderr :
399
- terminalreporter .write_line (results .stderr , yellow = True )
398
+ else :
399
+ for note in (
400
+ unreported_note
401
+ for errors in results .abspath_errors .values ()
402
+ if all (_error_severity (error ) == "note" for error in errors )
403
+ for unreported_note in errors
404
+ ):
405
+ terminalreporter .write_line (note )
406
+ if results .unmatched_stdout :
407
+ color = {"red" : True } if results .status else {"green" : True }
408
+ terminalreporter .write_line (results .unmatched_stdout , ** color )
409
+ if results .stderr :
410
+ terminalreporter .write_line (results .stderr , yellow = True )
400
411
401
412
def pytest_unconfigure (self , config : pytest .Config ) -> None :
402
413
"""Clean up the mypy results path."""
0 commit comments