@@ -1106,22 +1106,21 @@ def test_ternary_display():
11061106
11071107
11081108class TestIssue2121 :
1109- def test_simple (self , testdir ):
1110- testdir .tmpdir .join ("tests/file.py" ).ensure ().write (
1111- """
1112- def test_simple_failure():
1113- assert 1 + 1 == 3
1114- """
1115- )
1116- testdir .tmpdir .join ("pytest.ini" ).write (
1117- textwrap .dedent (
1109+ def test_rewrite_python_files_contain_subdirs (self , testdir ):
1110+ testdir .makepyfile (
1111+ ** {
1112+ "tests/file.py" : """
1113+ def test_simple_failure():
1114+ assert 1 + 1 == 3
11181115 """
1119- [pytest]
1120- python_files = tests/**.py
1121- """
1122- )
1116+ }
1117+ )
1118+ testdir .makeini (
1119+ """
1120+ [pytest]
1121+ python_files = tests/**.py
1122+ """
11231123 )
1124-
11251124 result = testdir .runpytest ()
11261125 result .stdout .fnmatch_lines ("*E*assert (1 + 1) == 3" )
11271126
@@ -1153,3 +1152,83 @@ def spy_write_pyc(*args, **kwargs):
11531152 hook = AssertionRewritingHook (pytestconfig )
11541153 assert hook .find_module ("test_foo" ) is not None
11551154 assert len (write_pyc_called ) == 1
1155+
1156+
1157+ class TestEarlyRewriteBailout (object ):
1158+ @pytest .fixture
1159+ def hook (self , pytestconfig , monkeypatch , testdir ):
1160+ """Returns a patched AssertionRewritingHook instance so we can configure its initial paths and track
1161+ if imp.find_module has been called.
1162+ """
1163+ import imp
1164+
1165+ self .find_module_calls = []
1166+ self .initial_paths = set ()
1167+
1168+ class StubSession (object ):
1169+ _initialpaths = self .initial_paths
1170+
1171+ def isinitpath (self , p ):
1172+ return p in self ._initialpaths
1173+
1174+ def spy_imp_find_module (name , path ):
1175+ self .find_module_calls .append (name )
1176+ return imp .find_module (name , path )
1177+
1178+ hook = AssertionRewritingHook (pytestconfig )
1179+ # use default patterns, otherwise we inherit pytest's testing config
1180+ hook .fnpats [:] = ["test_*.py" , "*_test.py" ]
1181+ monkeypatch .setattr (hook , "_imp_find_module" , spy_imp_find_module )
1182+ hook .set_session (StubSession ())
1183+ testdir .syspathinsert ()
1184+ return hook
1185+
1186+ def test_basic (self , testdir , hook ):
1187+ """
1188+ Ensure we avoid calling imp.find_module when we know for sure a certain module will not be rewritten
1189+ to optimize assertion rewriting (#3918).
1190+ """
1191+ testdir .makeconftest (
1192+ """
1193+ import pytest
1194+ @pytest.fixture
1195+ def fix(): return 1
1196+ """
1197+ )
1198+ testdir .makepyfile (test_foo = "def test_foo(): pass" )
1199+ testdir .makepyfile (bar = "def bar(): pass" )
1200+ foobar_path = testdir .makepyfile (foobar = "def foobar(): pass" )
1201+ self .initial_paths .add (foobar_path )
1202+
1203+ # conftest files should always be rewritten
1204+ assert hook .find_module ("conftest" ) is not None
1205+ assert self .find_module_calls == ["conftest" ]
1206+
1207+ # files matching "python_files" mask should always be rewritten
1208+ assert hook .find_module ("test_foo" ) is not None
1209+ assert self .find_module_calls == ["conftest" , "test_foo" ]
1210+
1211+ # file does not match "python_files": early bailout
1212+ assert hook .find_module ("bar" ) is None
1213+ assert self .find_module_calls == ["conftest" , "test_foo" ]
1214+
1215+ # file is an initial path (passed on the command-line): should be rewritten
1216+ assert hook .find_module ("foobar" ) is not None
1217+ assert self .find_module_calls == ["conftest" , "test_foo" , "foobar" ]
1218+
1219+ def test_pattern_contains_subdirectories (self , testdir , hook ):
1220+ """If one of the python_files patterns contain subdirectories ("tests/**.py") we can't bailout early
1221+ because we need to match with the full path, which can only be found by calling imp.find_module.
1222+ """
1223+ p = testdir .makepyfile (
1224+ ** {
1225+ "tests/file.py" : """
1226+ def test_simple_failure():
1227+ assert 1 + 1 == 3
1228+ """
1229+ }
1230+ )
1231+ testdir .syspathinsert (p .dirpath ())
1232+ hook .fnpats [:] = ["tests/**.py" ]
1233+ assert hook .find_module ("file" ) is not None
1234+ assert self .find_module_calls == ["file" ]
0 commit comments