mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-13 15:40:05 +00:00
test(python): lock in cross-module type-reference binding (#4262)
#4262 reported that a type defined exactly once never bound across modules, leaving hundreds of per-file stubs. It no longer reproduces on v8: the reference binds through the re-export chain (and, absent any import, through the unique-stub rewire). Add regression coverage matching the reporter's node layout (config/settings/_models.py -> config_settings_models_settings) plus an import-free inheritance control, so the resolved behavior cannot silently regress. No production change required. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
77276583e0
commit
2ca713fc86
1 file changed
+81
@@ -0,0 +1,81 @@
|
||||
"""Regression lock-in for #4262: a cross-module reference to a type defined
|
||||
exactly once must bind to that single located definition, not dangle on a
|
||||
per-file stub.
|
||||
|
||||
The reporter saw a `Settings` class defined once at `config/settings/_models.py`
|
||||
(node `config_settings_models_settings`) with hundreds of per-file `Settings`
|
||||
stub nodes and zero links to the real class. On current `v8` the reference binds
|
||||
through the re-export chain (and, failing that, through the unique-stub rewire),
|
||||
so these tests pin that behavior against regressions.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from graphify.build import build_from_json
|
||||
from graphify.extract import extract
|
||||
|
||||
|
||||
def _edge_set(G):
|
||||
return {
|
||||
(d.get("relation"), d.get("_src", u), d.get("_tgt", v))
|
||||
for u, v, d in G.edges(data=True)
|
||||
}
|
||||
|
||||
|
||||
def _settings_stub_count(res: dict) -> int:
|
||||
return sum(
|
||||
1 for n in res["nodes"]
|
||||
if n.get("label") == "Settings" and not n.get("source_file")
|
||||
)
|
||||
|
||||
|
||||
def test_reexported_type_reference_binds_to_single_located_def(tmp_path: Path) -> None:
|
||||
(tmp_path / "config" / "settings").mkdir(parents=True)
|
||||
(tmp_path / "app").mkdir()
|
||||
(tmp_path / "config" / "__init__.py").write_text("", encoding="utf-8")
|
||||
(tmp_path / "config" / "settings" / "__init__.py").write_text(
|
||||
"from ._models import Settings\n", encoding="utf-8"
|
||||
)
|
||||
(tmp_path / "config" / "settings" / "_models.py").write_text(
|
||||
"class Settings:\n def load(self):\n return 1\n", encoding="utf-8"
|
||||
)
|
||||
(tmp_path / "app" / "service.py").write_text(
|
||||
"from config.settings import Settings\n\n\n"
|
||||
"def run(s: Settings):\n return s.load()\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
paths = [
|
||||
tmp_path / "config" / "__init__.py",
|
||||
tmp_path / "config" / "settings" / "__init__.py",
|
||||
tmp_path / "config" / "settings" / "_models.py",
|
||||
tmp_path / "app" / "service.py",
|
||||
]
|
||||
res = extract(paths, root=tmp_path, parallel=False, cache_root=tmp_path / "graphify-cache")
|
||||
|
||||
assert _settings_stub_count(res) == 0, "no sourceless per-file Settings stub should survive"
|
||||
G = build_from_json(res, root=str(tmp_path), directed=True)
|
||||
edges = _edge_set(G)
|
||||
assert ("references", "app_service_run", "config_settings_models_settings") in edges, (
|
||||
f"cross-module Settings reference did not bind to the single located def: {edges}"
|
||||
)
|
||||
|
||||
|
||||
def test_unresolved_type_references_coalesce_to_unique_def(tmp_path: Path) -> None:
|
||||
"""No imports at all: several files inherit a `Settings` defined exactly once.
|
||||
The unique-stub rewire must bind every reference to the one real class rather
|
||||
than leaving a per-file stub in each file."""
|
||||
(tmp_path / "pkg").mkdir()
|
||||
(tmp_path / "pkg" / "_models.py").write_text("class Settings:\n pass\n", encoding="utf-8")
|
||||
(tmp_path / "a.py").write_text("class A(Settings):\n pass\n", encoding="utf-8")
|
||||
(tmp_path / "b.py").write_text("class B(Settings):\n pass\n", encoding="utf-8")
|
||||
|
||||
paths = [tmp_path / "pkg" / "_models.py", tmp_path / "a.py", tmp_path / "b.py"]
|
||||
res = extract(paths, root=tmp_path, parallel=False, cache_root=tmp_path / "graphify-cache")
|
||||
|
||||
assert _settings_stub_count(res) == 0
|
||||
G = build_from_json(res, root=str(tmp_path), directed=True)
|
||||
edges = _edge_set(G)
|
||||
assert ("inherits", "a_a", "pkg_models_settings") in edges
|
||||
assert ("inherits", "b_b", "pkg_models_settings") in edges
|
||||
Reference in new issue
Block a user