File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -397,9 +397,28 @@ def _read_pyc(
397397 if not isinstance (co , types .CodeType ):
398398 trace (f"_read_pyc({ source } ): not a code object" )
399399 return None
400+ # A cached pyc can be moved together with the source file (for example
401+ # by renaming a package or test directory). In that case, the marshaled
402+ # code object's ``co_filename`` still points to the old source path,
403+ # which leaks stale filenames in tracebacks and ``inspect``.
404+ source_str = str (source )
405+ if co .co_filename != source_str :
406+ co = _replace_code_filenames (co , source_str )
400407 return co
401408
402409
410+ def _replace_code_filenames (co : types .CodeType , filename : str ) -> types .CodeType :
411+ return co .replace (
412+ co_filename = filename ,
413+ co_consts = tuple (
414+ _replace_code_filenames (const , filename )
415+ if isinstance (const , types .CodeType )
416+ else const
417+ for const in co .co_consts
418+ ),
419+ )
420+
421+
403422def rewrite_asserts (
404423 mod : ast .Module ,
405424 source : bytes ,
Original file line number Diff line number Diff line change @@ -1138,6 +1138,31 @@ def test_foo():
11381138 glob .glob ("__pycache__/*.pyc" )
11391139 )
11401140
1141+ def test_moved_test_file_updates_code_filename (
1142+ self , pytester : Pytester , monkeypatch : pytest .MonkeyPatch
1143+ ) -> None :
1144+ """Moving a test module must keep ``co_filename`` synchronized with ``__file__``."""
1145+ monkeypatch .delenv ("PYTHONPYCACHEPREFIX" , raising = False )
1146+
1147+ pytester .makepyfile (
1148+ ** {
1149+ "test1/test_a.py" : """
1150+ from inspect import currentframe
1151+
1152+ def test_a():
1153+ assert currentframe().f_code.co_filename == __file__
1154+ """
1155+ }
1156+ )
1157+
1158+ first = pytester .runpytest_subprocess ("-s" , "test1/test_a.py" )
1159+ first .assert_outcomes (passed = 1 )
1160+
1161+ pytester .path .joinpath ("test1" ).rename (pytester .path .joinpath ("test2" ))
1162+
1163+ second = pytester .runpytest_subprocess ("-s" , "test2/test_a.py" )
1164+ second .assert_outcomes (passed = 1 )
1165+
11411166 @pytest .mark .skipif ('"__pypy__" in sys.modules' )
11421167 def test_pyc_vs_pyo (
11431168 self ,
You can’t perform that action at this time.
0 commit comments