From 4e08f5c1b3aa6f0a133d1d88165946c8c3bae89d Mon Sep 17 00:00:00 2001 From: "Johnny.H" Date: Thu, 13 Mar 2025 15:04:27 +0800 Subject: [PATCH 1/4] fix https://github.com/pytest-dev/pytest/issues/13292 --- src/_pytest/assertion/rewrite.py | 4 ++-- testing/test_assertrewrite.py | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 2e606d1903a..c4f86300bd1 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -317,7 +317,7 @@ def _write_pyc_fp( flags = b"\x00\x00\x00\x00" fp.write(flags) # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) - mtime = int(source_stat.st_mtime) & 0xFFFFFFFF + mtime = source_stat.st_mtime_ns & 0xFFFFFFFF size = source_stat.st_size & 0xFFFFFFFF # " None: mtime = b"\x58\x3c\xb0\x5f" mtime_int = int.from_bytes(mtime, "little") - os.utime(source, (mtime_int, mtime_int)) + os.utime(source, ns=(mtime_int, mtime_int)) size = len(source_bytes).to_bytes(4, "little") @@ -2374,3 +2374,25 @@ def test_saferepr_unbounded(self): _saferepr(self.Help) == f"" ) + + +class TestIssue13292: + def test_load_cache_based_on_file_st_mtime_ns(self, pytester: Pytester) -> None: + pytester.makepyfile(""" + def test_dummy1(): + def func(): + pass + print(func.__qualname__) + """) + r = pytester.runpytest("-s") + assert r.ret == 0 + assert "test_dummy1..func" in r.stdout.str() + pytester.makepyfile(""" + def test_dummy2(): + def func(): + pass + print(func.__qualname__) + """) + r = pytester.runpytest("-s") + assert r.ret == 0 + assert "test_dummy2..func" in r.stdout.str() From e4d70c72726754e9639a7ce61017b789cd28e925 Mon Sep 17 00:00:00 2001 From: "Johnny.H" Date: Thu, 13 Mar 2025 15:08:39 +0800 Subject: [PATCH 2/4] fix https://github.com/pytest-dev/pytest/issues/13292 --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 95e6b13f11e..731f912178b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -488,3 +488,4 @@ Zachary OBrien Zhouxin Qiu Zoltán Máté Zsolt Cserna +Johnny Huang From c573aa4e9932c213bfac3adf428afe7ac3fa7aa3 Mon Sep 17 00:00:00 2001 From: "Johnny.H" Date: Fri, 14 Mar 2025 10:33:21 +0800 Subject: [PATCH 3/4] Add changelog --- changelog/13292.bugfix.rst | 1 + testing/test_assertrewrite.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changelog/13292.bugfix.rst diff --git a/changelog/13292.bugfix.rst b/changelog/13292.bugfix.rst new file mode 100644 index 00000000000..c60cfd53ac9 --- /dev/null +++ b/changelog/13292.bugfix.rst @@ -0,0 +1 @@ +Fixed stale pyc cache loading after rapid test file updates. diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index c072902328d..c19181cbeb3 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -2377,7 +2377,12 @@ def test_saferepr_unbounded(self): class TestIssue13292: - def test_load_cache_based_on_file_st_mtime_ns(self, pytester: Pytester) -> None: + """ + Check the pyc cache generated in the 1st test execution + is not loaded in the 2nd test execution. + """ + + def test_stale_pyc_cache_is_not_loaded(self, pytester: Pytester) -> None: pytester.makepyfile(""" def test_dummy1(): def func(): From 243121137452bca3c5951055ed8ac3350f1604a0 Mon Sep 17 00:00:00 2001 From: "Johnny.H" Date: Fri, 14 Mar 2025 12:47:15 +0800 Subject: [PATCH 4/4] fix ut in `Windows` platform --- testing/test_assertrewrite.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index c19181cbeb3..c5770bb243e 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1381,10 +1381,13 @@ def test_read_pyc_more_invalid(self, tmp_path: Path) -> None: flags = b"\x00\x00\x00\x00" - mtime = b"\x58\x3c\xb0\x5f" + mtime = b"\x98\xf34\x10\x91\x8b,\x18" mtime_int = int.from_bytes(mtime, "little") + # set mtime_ns for win requires integer nanoseconds os.utime(source, ns=(mtime_int, mtime_int)) + mtime = (mtime_int & 0xFFFFFFFF).to_bytes(4, "little") + size = len(source_bytes).to_bytes(4, "little") code = marshal.dumps(compile(source_bytes, str(source), "exec"))