11import  signal 
22import  textwrap 
33
4+ import  pexpect 
45import  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  
@@ -243,20 +247,39 @@ def pytest_configure(config):
243247    assert  result .ret  ==  0 
244248
245249
246- def  test_mypy_indirect (testdir , xdist_args ):
250+ @pytest .mark .parametrize ( 
251+     "module_name" , 
252+     [ 
253+         pytest .param ( 
254+             "__init__" , 
255+             marks = pytest .mark .xfail ( 
256+                 (3 , 10 ) <=  PYTEST_VERSION  <  (6 , 2 ), 
257+                 raises = AssertionError , 
258+                 reason = "https://github.com/pytest-dev/pytest/issues/8016" , 
259+             ), 
260+         ), 
261+         "good" , 
262+     ], 
263+ ) 
264+ def  test_mypy_indirect (testdir , xdist_args , module_name ):
247265    """Verify that uncollected files checked by mypy cause a failure.""" 
248266    testdir .makepyfile (
249267        bad = """ 
250268            def pyfunc(x: int) -> str: 
251269                return x * 2 
252270        """ ,
253271    )
254-     testdir .makepyfile (
255-         good = """ 
256-             import bad 
257-         """ ,
272+     pyfile  =  testdir .makepyfile (
273+         ** {
274+             module_name : """ 
275+                 import bad 
276+             """ ,
277+         },
258278    )
259-     result  =  testdir .runpytest_subprocess ("--mypy" , * xdist_args , "good.py" )
279+     result  =  testdir .runpytest_subprocess ("--mypy" , * xdist_args , str (pyfile ))
280+     mypy_file_checks  =  1 
281+     mypy_status_check  =  1 
282+     result .assert_outcomes (passed = mypy_file_checks , failed = mypy_status_check )
260283    assert  result .ret  !=  0 
261284
262285
@@ -309,7 +332,8 @@ def pyfunc(x):
309332    assert  result .ret  !=  0 
310333
311334
312- def  test_looponfail (testdir ):
335+ @pytest .mark .parametrize ("module_name" , ["__init__" , "test_demo" ]) 
336+ def  test_looponfail (testdir , module_name ):
313337    """Ensure that the plugin works with --looponfail.""" 
314338
315339    pass_source  =  textwrap .dedent (
@@ -324,7 +348,7 @@ def pyfunc(x: int) -> str:
324348            return x * 2 
325349        """ ,
326350    )
327-     pyfile  =  testdir .makepyfile (fail_source )
351+     pyfile  =  testdir .makepyfile (** { module_name :  fail_source } )
328352    looponfailroot  =  testdir .mkdir ("looponfailroot" )
329353    looponfailroot_pyfile  =  looponfailroot .join (pyfile .basename )
330354    pyfile .move (looponfailroot_pyfile )
@@ -345,6 +369,14 @@ def pyfunc(x: int) -> str:
345369        expect_timeout = 30.0 ,
346370    )
347371
372+     num_tests  =  2 
373+     if  module_name  ==  "__init__"  and  (3 , 10 ) <=  PYTEST_VERSION  <  (6 , 2 ):
374+         # https://github.com/pytest-dev/pytest/issues/8016 
375+         # Pytest had a bug where it assumed only a Package would have a basename of 
376+         # __init__.py. In this test, Pytest mistakes MypyFile for a Package and 
377+         # returns after collecting only one object (the MypyFileItem). 
378+         num_tests  =  1 
379+ 
348380    def  _expect_session ():
349381        child .expect ("==== test session starts ====" )
350382
@@ -353,10 +385,11 @@ def _expect_failure():
353385        child .expect ("==== FAILURES ====" )
354386        child .expect (pyfile .basename  +  " ____" )
355387        child .expect ("2: error: Incompatible return value" )
356-         # These only show with mypy>=0.730: 
357-         # child.expect("==== mypy ====") 
358-         # child.expect("Found 1 error in 1 file (checked 1 source file)") 
359-         child .expect ("2 failed" )
388+         # if num_tests == 2: 
389+         #     # These only show with mypy>=0.730: 
390+         #     child.expect("==== mypy ====") 
391+         #     child.expect("Found 1 error in 1 file (checked 1 source file)") 
392+         child .expect (str (num_tests ) +  " failed" )
360393        child .expect ("#### LOOPONFAILING ####" )
361394        _expect_waiting ()
362395
@@ -375,10 +408,27 @@ def _expect_changed():
375408    def  _expect_success ():
376409        for  _  in  range (2 ):
377410            _expect_session ()
378-             # These only show with mypy>=0.730: 
379-             # child.expect("==== mypy ====") 
380-             # child.expect("Success: no issues found in 1 source file") 
381-             child .expect ("2 passed" )
411+             # if num_tests == 2: 
412+             #     # These only show with mypy>=0.730: 
413+             #     child.expect("==== mypy ====") 
414+             #     child.expect("Success: no issues found in 1 source file") 
415+             try :
416+                 child .expect (str (num_tests ) +  " passed" )
417+             except  pexpect .exceptions .TIMEOUT :
418+                 if  module_name  ==  "__init__"  and  (6 , 0 ) <=  PYTEST_VERSION  <  (6 , 2 ):
419+                     # MypyItems hit the __init__.py bug too when --looponfail 
420+                     # re-collects them after the failing file is modified. 
421+                     # Unlike MypyFile, MypyItem is not a Collector, so this used 
422+                     # to cause an AttributeError until a workaround was added 
423+                     # (MypyItem.collect was defined to yield itself). 
424+                     # Mypy probably noticed the __init__.py problem during the 
425+                     # development of Pytest 6.0, but the error was addressed 
426+                     # with an isinstance assertion, which broke the workaround. 
427+                     # Here, we hit that assertion: 
428+                     child .expect ("AssertionError" )
429+                     child .expect ("1 error" )
430+                     pytest .xfail ("https://github.com/pytest-dev/pytest/issues/8016" )
431+                 raise 
382432        _expect_waiting ()
383433
384434    def  _break ():
@@ -391,3 +441,27 @@ def _break():
391441    _break ()
392442    _fix ()
393443    child .kill (signal .SIGTERM )
444+ 
445+ 
446+ def  test_mypy_item_collect (testdir , xdist_args ):
447+     """Ensure coverage for a 3.10<=pytest<6.0 workaround.""" 
448+     testdir .makepyfile (
449+         """ 
450+         def test_mypy_item_collect(request): 
451+             plugin = request.config.pluginmanager.getplugin("mypy") 
452+             mypy_items = [ 
453+                 item 
454+                 for item in request.session.items 
455+                 if isinstance(item, plugin.MypyItem) 
456+             ] 
457+             assert mypy_items 
458+             for mypy_item in mypy_items: 
459+                 assert all(item is mypy_item for item in mypy_item.collect()) 
460+         """ ,
461+     )
462+     result  =  testdir .runpytest_subprocess ("--mypy" , * xdist_args )
463+     test_count  =  1 
464+     mypy_file_checks  =  1 
465+     mypy_status_check  =  1 
466+     result .assert_outcomes (passed = test_count  +  mypy_file_checks  +  mypy_status_check )
467+     assert  result .ret  ==  0 
0 commit comments