From 09be2947d3a7bfe9d4c451b05c31fb9f658fd7ed Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 12:38:52 +0000 Subject: [PATCH 01/10] Fix dependency calculation in diff-cache.py tool The diff was calculated the wrong way around. --- misc/diff-cache.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/diff-cache.py b/misc/diff-cache.py index 8f8ab19f21cf..7e33e17fd909 100644 --- a/misc/diff-cache.py +++ b/misc/diff-cache.py @@ -142,9 +142,9 @@ def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--verbose", action="store_true", default=False, help="Increase verbosity") parser.add_argument("--sqlite", action="store_true", default=False, help="Use a sqlite cache") - parser.add_argument("input_dir1", help="Input directory for the cache") - parser.add_argument("input_dir2", help="Input directory for the cache") - parser.add_argument("output", help="Output file") + parser.add_argument("input_dir1", help="Input directory for the original cache") + parser.add_argument("input_dir2", help="Input directory for the target cache") + parser.add_argument("output", help="Output file with the diff from original cache") args = parser.parse_args() cache1 = make_cache(args.input_dir1, args.sqlite) @@ -199,7 +199,7 @@ def main() -> None: # Compute what deps have been added and merge them all into the # @root deps file. - new_deps = {k: deps1.get(k, set()) - deps2.get(k, set()) for k in deps2} + new_deps = {k: deps2.get(k, set()) - deps1.get(k, set()) for k in deps2} new_deps = {k: v for k, v in new_deps.items() if v} try: root_deps = load(cache1, "@root.deps.json") From be856fabf0cf67fb57018eb2de1d80a4d1cfff8a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 14:50:37 +0000 Subject: [PATCH 02/10] WIP add test --- mypy/test/test_diff_cache.py | 106 +++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 mypy/test/test_diff_cache.py diff --git a/mypy/test/test_diff_cache.py b/mypy/test/test_diff_cache.py new file mode 100644 index 000000000000..30bdb1676f94 --- /dev/null +++ b/mypy/test/test_diff_cache.py @@ -0,0 +1,106 @@ +"""Tests for misc/diff-cache.py.""" + +from __future__ import annotations + +import json +import os +import shutil +import subprocess +import sys +import tempfile +import time +import unittest + +_DIFF_CACHE_PATH = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "misc", + "diff-cache.py", +) + + +class DiffCacheIntegrationTests(unittest.TestCase): + """Integration test: run mypy twice with different sources, then diff the caches.""" + + def test_diff_cache_produces_valid_json(self) -> None: + # Use a single source directory with two cache directories so that + # source paths in the cache metadata are identical between runs. + # Only b.py changes between the two runs. + src_dir = tempfile.mkdtemp() + output_file = os.path.join(tempfile.mkdtemp(), "diff.json") + try: + cache1 = os.path.join(src_dir, "cache1") + cache2 = os.path.join(src_dir, "cache2") + + # Write sources and run mypy for cache1 + with open(os.path.join(src_dir, "a.py"), "w") as f: + f.write("x: int = 1\n") + with open(os.path.join(src_dir, "b.py"), "w") as f: + f.write("import a\ndef foo() -> int:\n return 1\n") + result = subprocess.run( + [sys.executable, "-m", "mypy", "--cache-fine-grained", + "--cache-dir", cache1, "b.py"], + cwd=src_dir, + capture_output=True, + text=True, + ) + self.assertEqual(result.returncode, 0, f"mypy run 1 failed: {result.stderr}") + + # Sleep so that mtimes will be different between runs + time.sleep(1) + + # Touch a.py to change its mtime without modifying content + os.utime(os.path.join(src_dir, "a.py")) + + # Modify b.py and run mypy for cache2 + with open(os.path.join(src_dir, "b.py"), "w") as f: + f.write("import a\ndef foo() -> str:\n return 'hello'\n") + result = subprocess.run( + [sys.executable, "-m", "mypy", "--cache-fine-grained", + "--cache-dir", cache2, "b.py"], + cwd=src_dir, + capture_output=True, + text=True, + ) + self.assertEqual(result.returncode, 0, f"mypy run 2 failed: {result.stderr}") + + # Find the Python version subdirectory (e.g. "3.14") + subdirs = [ + e for e in os.listdir(cache1) + if os.path.isdir(os.path.join(cache1, e)) and e[0].isdigit() + ] + self.assertEqual(len(subdirs), 1, f"Expected one version subdir, got {subdirs}") + ver = subdirs[0] + + # Run diff-cache.py + result = subprocess.run( + [sys.executable, _DIFF_CACHE_PATH, + os.path.join(cache1, ver), os.path.join(cache2, ver), output_file], + capture_output=True, + text=True, + ) + self.assertEqual( + result.returncode, 0, + f"diff-cache.py failed: {result.stderr}", + ) + + # Verify the output is valid JSON + with open(output_file) as f: + data = json.load(f) + self.assertIsInstance(data, dict) + self.assertGreater(len(data), 0, "Expected non-empty diff") + + # Only modified files should appear in the diff. + # b.py changed, so b.meta.ff, b.data.ff, and b.deps.json should be present. + # a.py did not change, so no a.* keys should appear. + keys = set(data.keys()) + b_keys = {k for k in keys if "/b." in k or k.startswith("b.")} + a_keys = {k for k in keys if "/a." in k or k.startswith("a.")} + self.assertTrue(b_keys, f"Expected b.* entries in diff, got keys: {keys}") + self.assertFalse(a_keys, f"Unexpected a.* entries in diff: {a_keys}") + finally: + shutil.rmtree(src_dir, ignore_errors=True) + shutil.rmtree(os.path.dirname(output_file), ignore_errors=True) + + +if __name__ == "__main__": + unittest.main() From 084a7cc83d9d58ce1f11c8c7291aa99d06cabf66 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 14:57:58 +0000 Subject: [PATCH 03/10] Update test --- mypy/test/test_diff_cache.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/mypy/test/test_diff_cache.py b/mypy/test/test_diff_cache.py index 30bdb1676f94..df15993dd19f 100644 --- a/mypy/test/test_diff_cache.py +++ b/mypy/test/test_diff_cache.py @@ -43,7 +43,7 @@ def test_diff_cache_produces_valid_json(self) -> None: capture_output=True, text=True, ) - self.assertEqual(result.returncode, 0, f"mypy run 1 failed: {result.stderr}") + assert result.returncode == 0, f"mypy run 1 failed: {result.stderr}" # Sleep so that mtimes will be different between runs time.sleep(1) @@ -51,24 +51,26 @@ def test_diff_cache_produces_valid_json(self) -> None: # Touch a.py to change its mtime without modifying content os.utime(os.path.join(src_dir, "a.py")) - # Modify b.py and run mypy for cache2 + # Modify b.py and add a new c.py, then run mypy for cache2 with open(os.path.join(src_dir, "b.py"), "w") as f: f.write("import a\ndef foo() -> str:\n return 'hello'\n") + with open(os.path.join(src_dir, "c.py"), "w") as f: + f.write("import a\ny: str = 'world'\n") result = subprocess.run( [sys.executable, "-m", "mypy", "--cache-fine-grained", - "--cache-dir", cache2, "b.py"], + "--cache-dir", cache2, "b.py", "c.py"], cwd=src_dir, capture_output=True, text=True, ) - self.assertEqual(result.returncode, 0, f"mypy run 2 failed: {result.stderr}") + assert result.returncode == 0, f"mypy run 2 failed: {result.stderr}" # Find the Python version subdirectory (e.g. "3.14") subdirs = [ e for e in os.listdir(cache1) if os.path.isdir(os.path.join(cache1, e)) and e[0].isdigit() ] - self.assertEqual(len(subdirs), 1, f"Expected one version subdir, got {subdirs}") + assert len(subdirs) == 1, f"Expected one version subdir, got {subdirs}" ver = subdirs[0] # Run diff-cache.py @@ -78,25 +80,24 @@ def test_diff_cache_produces_valid_json(self) -> None: capture_output=True, text=True, ) - self.assertEqual( - result.returncode, 0, - f"diff-cache.py failed: {result.stderr}", - ) + assert result.returncode == 0, f"diff-cache.py failed: {result.stderr}" # Verify the output is valid JSON with open(output_file) as f: data = json.load(f) - self.assertIsInstance(data, dict) - self.assertGreater(len(data), 0, "Expected non-empty diff") + assert isinstance(data, dict) + assert len(data) > 0, "Expected non-empty diff" - # Only modified files should appear in the diff. - # b.py changed, so b.meta.ff, b.data.ff, and b.deps.json should be present. + # Only modified or new files should appear in the diff. + # b.py changed and c.py is new, so both should be present. # a.py did not change, so no a.* keys should appear. keys = set(data.keys()) b_keys = {k for k in keys if "/b." in k or k.startswith("b.")} + c_keys = {k for k in keys if "/c." in k or k.startswith("c.")} a_keys = {k for k in keys if "/a." in k or k.startswith("a.")} - self.assertTrue(b_keys, f"Expected b.* entries in diff, got keys: {keys}") - self.assertFalse(a_keys, f"Unexpected a.* entries in diff: {a_keys}") + assert len(a_keys) == 0, f"Unexpected a.* entries in diff: {a_keys}" + assert len(b_keys) == 2, f"Expected 2 b.* entries in diff, got: {b_keys}" + assert len(c_keys) == 3, f"Expected 3 c.* entries in diff, got: {c_keys}" finally: shutil.rmtree(src_dir, ignore_errors=True) shutil.rmtree(os.path.dirname(output_file), ignore_errors=True) From f7253361e17f889d68f608ee257699b62e712af8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 15:07:41 +0000 Subject: [PATCH 04/10] Use sqlite cache --- mypy/test/test_diff_cache.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypy/test/test_diff_cache.py b/mypy/test/test_diff_cache.py index df15993dd19f..af85388e98e8 100644 --- a/mypy/test/test_diff_cache.py +++ b/mypy/test/test_diff_cache.py @@ -31,13 +31,13 @@ def test_diff_cache_produces_valid_json(self) -> None: cache1 = os.path.join(src_dir, "cache1") cache2 = os.path.join(src_dir, "cache2") - # Write sources and run mypy for cache1 + # Write sources and run mypy for cache1 (using sqlite cache) with open(os.path.join(src_dir, "a.py"), "w") as f: f.write("x: int = 1\n") with open(os.path.join(src_dir, "b.py"), "w") as f: f.write("import a\ndef foo() -> int:\n return 1\n") result = subprocess.run( - [sys.executable, "-m", "mypy", "--cache-fine-grained", + [sys.executable, "-m", "mypy", "--sqlite-cache", "--cache-fine-grained", "--cache-dir", cache1, "b.py"], cwd=src_dir, capture_output=True, @@ -57,7 +57,7 @@ def test_diff_cache_produces_valid_json(self) -> None: with open(os.path.join(src_dir, "c.py"), "w") as f: f.write("import a\ny: str = 'world'\n") result = subprocess.run( - [sys.executable, "-m", "mypy", "--cache-fine-grained", + [sys.executable, "-m", "mypy", "--sqlite-cache", "--cache-fine-grained", "--cache-dir", cache2, "b.py", "c.py"], cwd=src_dir, capture_output=True, @@ -73,9 +73,9 @@ def test_diff_cache_produces_valid_json(self) -> None: assert len(subdirs) == 1, f"Expected one version subdir, got {subdirs}" ver = subdirs[0] - # Run diff-cache.py + # Run diff-cache.py with --sqlite result = subprocess.run( - [sys.executable, _DIFF_CACHE_PATH, + [sys.executable, _DIFF_CACHE_PATH, "--sqlite", os.path.join(cache1, ver), os.path.join(cache2, ver), output_file], capture_output=True, text=True, From 79c5e386992db46ae0f20d328e1114b49127ee36 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 15:13:06 +0000 Subject: [PATCH 05/10] Also test deps --- mypy/test/test_diff_cache.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/mypy/test/test_diff_cache.py b/mypy/test/test_diff_cache.py index af85388e98e8..c5cd83b53ed4 100644 --- a/mypy/test/test_diff_cache.py +++ b/mypy/test/test_diff_cache.py @@ -51,9 +51,10 @@ def test_diff_cache_produces_valid_json(self) -> None: # Touch a.py to change its mtime without modifying content os.utime(os.path.join(src_dir, "a.py")) - # Modify b.py and add a new c.py, then run mypy for cache2 + # Modify b.py to access a.x (adding a fine-grained dependency), + # and add a new c.py, then run mypy for cache2 with open(os.path.join(src_dir, "b.py"), "w") as f: - f.write("import a\ndef foo() -> str:\n return 'hello'\n") + f.write("import a\ndef foo() -> str:\n return str(a.x)\n") with open(os.path.join(src_dir, "c.py"), "w") as f: f.write("import a\ny: str = 'world'\n") result = subprocess.run( @@ -98,6 +99,16 @@ def test_diff_cache_produces_valid_json(self) -> None: assert len(a_keys) == 0, f"Unexpected a.* entries in diff: {a_keys}" assert len(b_keys) == 2, f"Expected 2 b.* entries in diff, got: {b_keys}" assert len(c_keys) == 3, f"Expected 3 c.* entries in diff, got: {c_keys}" + + # The new access to a.x in b.py should create a fine-grained + # dependency recorded in @root.deps.json. + assert "@root.deps.json" in keys + root_deps = json.loads(data["@root.deps.json"]) + assert set(root_deps.keys()) == {"", ""}, ( + f"Unexpected root deps keys: {sorted(root_deps.keys())}" + ) + assert sorted(root_deps[""]) == ["b.foo"] + assert sorted(root_deps[""]) == ["b.foo", "c"] finally: shutil.rmtree(src_dir, ignore_errors=True) shutil.rmtree(os.path.dirname(output_file), ignore_errors=True) From 04a33fcbfda2a6314d27bffe37f5672e9e9eac9d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 15:24:53 +0000 Subject: [PATCH 06/10] Test apply cache diff --- mypy/test/test_diff_cache.py | 68 ++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/mypy/test/test_diff_cache.py b/mypy/test/test_diff_cache.py index c5cd83b53ed4..f83bc18a68fc 100644 --- a/mypy/test/test_diff_cache.py +++ b/mypy/test/test_diff_cache.py @@ -11,11 +11,12 @@ import time import unittest -_DIFF_CACHE_PATH = os.path.join( +_MISC_DIR = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "misc", - "diff-cache.py", ) +_DIFF_CACHE_PATH = os.path.join(_MISC_DIR, "diff-cache.py") +_APPLY_CACHE_DIFF_PATH = os.path.join(_MISC_DIR, "apply-cache-diff.py") class DiffCacheIntegrationTests(unittest.TestCase): @@ -109,6 +110,69 @@ def test_diff_cache_produces_valid_json(self) -> None: ) assert sorted(root_deps[""]) == ["b.foo"] assert sorted(root_deps[""]) == ["b.foo", "c"] + + # Apply the diff to a copy of cache1 and verify the result. + cache1_ver = os.path.join(cache1, ver) + cache2_ver = os.path.join(cache2, ver) + patched = os.path.join(src_dir, "patched") + patched_ver = os.path.join(patched, ver) + shutil.copytree(cache1, patched) + + # Snapshot cache entries before applying the diff + from mypy.metastore import SqliteMetadataStore + + def read_all(cache_dir: str) -> dict[str, bytes]: + store = SqliteMetadataStore(cache_dir) + result = {name: store.read(name) for name in store.list_all()} + assert store.db is not None + store.db.close() + return result + + before = read_all(patched_ver) + + # Apply the diff + result = subprocess.run( + [sys.executable, _APPLY_CACHE_DIFF_PATH, "--sqlite", + patched_ver, output_file], + capture_output=True, + text=True, + ) + assert result.returncode == 0, f"apply-cache-diff.py failed: {result.stderr}" + + after = read_all(patched_ver) + + # a.py entries should be unchanged + for name in before: + if name.startswith("a.") or "/a." in name: + assert name in after, f"{name} missing after apply" + assert before[name] == after[name], f"{name} changed after apply" + + # b.py and c.py entries should match cache2 after applying the diff. + # Skip .meta.ff files since they contain mtimes that legitimately differ. + target = read_all(cache2_ver) + for prefix in ("b.", "c."): + for name in target: + if not (name.startswith(prefix) or f"/{prefix}" in name): + continue + assert name in after, f"{name} missing after apply" + if name.endswith(".meta.ff"): + # mtimes legitimately differ, but content should not be identical + # to the pre-apply version (it was updated by the diff) + assert after[name] != before.get(name), ( + f"{name} unchanged after apply" + ) + else: + assert after[name] == target[name], f"{name} differs from target" + + # Verify fine-grained deps were applied correctly + from mypy.util import json_loads + + applied_root_deps = json_loads(after["@root.deps.json"]) + assert set(applied_root_deps.keys()) == {"", ""}, ( + f"Unexpected applied root deps keys: {sorted(applied_root_deps.keys())}" + ) + assert sorted(applied_root_deps[""]) == ["b.foo"] + assert sorted(applied_root_deps[""]) == ["b.foo", "c"] finally: shutil.rmtree(src_dir, ignore_errors=True) shutil.rmtree(os.path.dirname(output_file), ignore_errors=True) From d2a5fb0df88f82e6a6fa0066e431ceccbe8b7c84 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 15:26:00 +0000 Subject: [PATCH 07/10] Lint --- mypy/test/test_diff_cache.py | 63 ++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/mypy/test/test_diff_cache.py b/mypy/test/test_diff_cache.py index f83bc18a68fc..bb3fafaeb4fa 100644 --- a/mypy/test/test_diff_cache.py +++ b/mypy/test/test_diff_cache.py @@ -12,8 +12,7 @@ import unittest _MISC_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "misc", + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "misc" ) _DIFF_CACHE_PATH = os.path.join(_MISC_DIR, "diff-cache.py") _APPLY_CACHE_DIFF_PATH = os.path.join(_MISC_DIR, "apply-cache-diff.py") @@ -38,8 +37,16 @@ def test_diff_cache_produces_valid_json(self) -> None: with open(os.path.join(src_dir, "b.py"), "w") as f: f.write("import a\ndef foo() -> int:\n return 1\n") result = subprocess.run( - [sys.executable, "-m", "mypy", "--sqlite-cache", "--cache-fine-grained", - "--cache-dir", cache1, "b.py"], + [ + sys.executable, + "-m", + "mypy", + "--sqlite-cache", + "--cache-fine-grained", + "--cache-dir", + cache1, + "b.py", + ], cwd=src_dir, capture_output=True, text=True, @@ -59,8 +66,17 @@ def test_diff_cache_produces_valid_json(self) -> None: with open(os.path.join(src_dir, "c.py"), "w") as f: f.write("import a\ny: str = 'world'\n") result = subprocess.run( - [sys.executable, "-m", "mypy", "--sqlite-cache", "--cache-fine-grained", - "--cache-dir", cache2, "b.py", "c.py"], + [ + sys.executable, + "-m", + "mypy", + "--sqlite-cache", + "--cache-fine-grained", + "--cache-dir", + cache2, + "b.py", + "c.py", + ], cwd=src_dir, capture_output=True, text=True, @@ -69,7 +85,8 @@ def test_diff_cache_produces_valid_json(self) -> None: # Find the Python version subdirectory (e.g. "3.14") subdirs = [ - e for e in os.listdir(cache1) + e + for e in os.listdir(cache1) if os.path.isdir(os.path.join(cache1, e)) and e[0].isdigit() ] assert len(subdirs) == 1, f"Expected one version subdir, got {subdirs}" @@ -77,8 +94,14 @@ def test_diff_cache_produces_valid_json(self) -> None: # Run diff-cache.py with --sqlite result = subprocess.run( - [sys.executable, _DIFF_CACHE_PATH, "--sqlite", - os.path.join(cache1, ver), os.path.join(cache2, ver), output_file], + [ + sys.executable, + _DIFF_CACHE_PATH, + "--sqlite", + os.path.join(cache1, ver), + os.path.join(cache2, ver), + output_file, + ], capture_output=True, text=True, ) @@ -105,14 +128,14 @@ def test_diff_cache_produces_valid_json(self) -> None: # dependency recorded in @root.deps.json. assert "@root.deps.json" in keys root_deps = json.loads(data["@root.deps.json"]) - assert set(root_deps.keys()) == {"", ""}, ( - f"Unexpected root deps keys: {sorted(root_deps.keys())}" - ) + assert set(root_deps.keys()) == { + "", + "", + }, f"Unexpected root deps keys: {sorted(root_deps.keys())}" assert sorted(root_deps[""]) == ["b.foo"] assert sorted(root_deps[""]) == ["b.foo", "c"] # Apply the diff to a copy of cache1 and verify the result. - cache1_ver = os.path.join(cache1, ver) cache2_ver = os.path.join(cache2, ver) patched = os.path.join(src_dir, "patched") patched_ver = os.path.join(patched, ver) @@ -132,8 +155,7 @@ def read_all(cache_dir: str) -> dict[str, bytes]: # Apply the diff result = subprocess.run( - [sys.executable, _APPLY_CACHE_DIFF_PATH, "--sqlite", - patched_ver, output_file], + [sys.executable, _APPLY_CACHE_DIFF_PATH, "--sqlite", patched_ver, output_file], capture_output=True, text=True, ) @@ -158,9 +180,7 @@ def read_all(cache_dir: str) -> dict[str, bytes]: if name.endswith(".meta.ff"): # mtimes legitimately differ, but content should not be identical # to the pre-apply version (it was updated by the diff) - assert after[name] != before.get(name), ( - f"{name} unchanged after apply" - ) + assert after[name] != before.get(name), f"{name} unchanged after apply" else: assert after[name] == target[name], f"{name} differs from target" @@ -168,9 +188,10 @@ def read_all(cache_dir: str) -> dict[str, bytes]: from mypy.util import json_loads applied_root_deps = json_loads(after["@root.deps.json"]) - assert set(applied_root_deps.keys()) == {"", ""}, ( - f"Unexpected applied root deps keys: {sorted(applied_root_deps.keys())}" - ) + assert set(applied_root_deps.keys()) == { + "", + "", + }, f"Unexpected applied root deps keys: {sorted(applied_root_deps.keys())}" assert sorted(applied_root_deps[""]) == ["b.foo"] assert sorted(applied_root_deps[""]) == ["b.foo", "c"] finally: From 366c6c161f3600c2d456fb0d8657d83b3a27497c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 15:30:58 +0000 Subject: [PATCH 08/10] Update comments and docstrings --- mypy/test/test_diff_cache.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/test/test_diff_cache.py b/mypy/test/test_diff_cache.py index bb3fafaeb4fa..bd17df877e2d 100644 --- a/mypy/test/test_diff_cache.py +++ b/mypy/test/test_diff_cache.py @@ -1,4 +1,4 @@ -"""Tests for misc/diff-cache.py.""" +"""Integration tests for misc/diff-cache.py and misc/apply-cache-diff.py.""" from __future__ import annotations @@ -19,12 +19,12 @@ class DiffCacheIntegrationTests(unittest.TestCase): - """Integration test: run mypy twice with different sources, then diff the caches.""" + """Run mypy twice with different sources, diff the caches, and apply the diff.""" def test_diff_cache_produces_valid_json(self) -> None: # Use a single source directory with two cache directories so that # source paths in the cache metadata are identical between runs. - # Only b.py changes between the two runs. + # b.py is modified and c.py is added in the second run. src_dir = tempfile.mkdtemp() output_file = os.path.join(tempfile.mkdtemp(), "diff.json") try: From 2010b3f41e22ea001c87a2356c7f866ab27d5d24 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 15:34:06 +0000 Subject: [PATCH 09/10] The new test suite should treated as a slow one --- runtests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtests.py b/runtests.py index 3f49107f3ce0..f79e7efa0ccb 100755 --- a/runtests.py +++ b/runtests.py @@ -20,6 +20,7 @@ MYPYC_SEPARATE = "TestRunSeparate" MYPYC_MULTIMODULE = "multimodule" # Subset of mypyc run tests that are slow ERROR_STREAM = "ErrorStreamSuite" +DIFF_CACHE = "DiffCacheIntegrationTests" ALL_NON_FAST = [ @@ -35,6 +36,7 @@ MYPYC_COMMAND_LINE, MYPYC_SEPARATE, ERROR_STREAM, + DIFF_CACHE, ] @@ -90,7 +92,7 @@ "pytest", "-q", "-k", - " or ".join([DAEMON, MYPYC_EXTERNAL, MYPYC_COMMAND_LINE, ERROR_STREAM]), + " or ".join([DAEMON, MYPYC_EXTERNAL, MYPYC_COMMAND_LINE, ERROR_STREAM, DIFF_CACHE]), ], "mypyc-fast": ["pytest", "-q", "mypyc", "-k", f"not ({' or '.join(MYPYC_SLOW)})"], # Test cases that might take minutes to run From a8e10e5dc342b8630fdeaa37c4ce84c193e04209 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 17 Feb 2026 15:37:45 +0000 Subject: [PATCH 10/10] Fix PYTHONPATH --- mypy/test/test_diff_cache.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mypy/test/test_diff_cache.py b/mypy/test/test_diff_cache.py index bd17df877e2d..a62c3a18c696 100644 --- a/mypy/test/test_diff_cache.py +++ b/mypy/test/test_diff_cache.py @@ -11,9 +11,9 @@ import time import unittest -_MISC_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "misc" -) +from mypy.test.config import PREFIX + +_MISC_DIR = os.path.join(PREFIX, "misc") _DIFF_CACHE_PATH = os.path.join(_MISC_DIR, "diff-cache.py") _APPLY_CACHE_DIFF_PATH = os.path.join(_MISC_DIR, "apply-cache-diff.py") @@ -27,6 +27,8 @@ def test_diff_cache_produces_valid_json(self) -> None: # b.py is modified and c.py is added in the second run. src_dir = tempfile.mkdtemp() output_file = os.path.join(tempfile.mkdtemp(), "diff.json") + env = os.environ.copy() + env["PYTHONPATH"] = PREFIX try: cache1 = os.path.join(src_dir, "cache1") cache2 = os.path.join(src_dir, "cache2") @@ -50,6 +52,7 @@ def test_diff_cache_produces_valid_json(self) -> None: cwd=src_dir, capture_output=True, text=True, + env=env, ) assert result.returncode == 0, f"mypy run 1 failed: {result.stderr}" @@ -80,6 +83,7 @@ def test_diff_cache_produces_valid_json(self) -> None: cwd=src_dir, capture_output=True, text=True, + env=env, ) assert result.returncode == 0, f"mypy run 2 failed: {result.stderr}" @@ -104,6 +108,7 @@ def test_diff_cache_produces_valid_json(self) -> None: ], capture_output=True, text=True, + env=env, ) assert result.returncode == 0, f"diff-cache.py failed: {result.stderr}" @@ -158,6 +163,7 @@ def read_all(cache_dir: str) -> dict[str, bytes]: [sys.executable, _APPLY_CACHE_DIFF_PATH, "--sqlite", patched_ver, output_file], capture_output=True, text=True, + env=env, ) assert result.returncode == 0, f"apply-cache-diff.py failed: {result.stderr}"