mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-25 16:22:30 +00:00
Carries the tests that belong with the module-call exact-name fix (#4577): the fix landed via the PR's follow-up perf commit, which did not include the test file. Adds the PR author's five cases (exact-name function/class resolution, aliased import, fold-only fail-closed) plus a negative cross-module case proving a call never binds to a same-named child of an UNRELATED module -- the one headline risk of re-bucketing by exact name. Co-Authored-By: madhusudanpatnaik <madhusudanpatnaik@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
75 lines
3.8 KiB
Python
75 lines
3.8 KiB
Python
"""`module.func()` resolves to the child named exactly `func` (#1883 module arm).
|
|
|
|
The module arm looked the callee up among the module's children by a key that
|
|
drops every non-alphanumeric character and folds case, so `dataclass_schema()`
|
|
and `class DataclassSchema` share one key. Python names are case-sensitive and
|
|
`_` is significant, yet any such pair made the lookup ambiguous and the call was
|
|
dropped: every `core_schema.x_schema(...)` call in pydantic-core (each function
|
|
sits beside its `XSchema` TypedDict) got no edge. Among children that share the
|
|
folded key, the one whose name is exactly the callee now wins. A call matching
|
|
only on the folded key, with no exact name, still gets no edge.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from graphify.extract import extract
|
|
|
|
_CORE = (
|
|
"from typing import TypedDict\n\n\n"
|
|
"class DataclassSchema(TypedDict):\n type: str\n\n\n"
|
|
"def dataclass_schema(cls):\n return {'type': 'dataclass'}\n\n\n"
|
|
"def compute_value():\n return 1\n\n\n"
|
|
"class ComputeValue:\n pass\n"
|
|
)
|
|
|
|
|
|
def _calls(tmp_path: Path, caller_body: str) -> list[tuple[str, str, str]]:
|
|
(tmp_path / "core_schema.py").write_text(_CORE, encoding="utf-8")
|
|
(tmp_path / "caller.py").write_text(caller_body, encoding="utf-8")
|
|
result = extract([tmp_path / "caller.py", tmp_path / "core_schema.py"], root=tmp_path,
|
|
cache_root=tmp_path / "graphify-out", parallel=False)
|
|
label = {n["id"]: n["label"] for n in result["nodes"]}
|
|
return [(label.get(e["source"], ""), label.get(e["target"], ""), e["confidence"])
|
|
for e in result["edges"]
|
|
if e["relation"] == "calls" and label.get(e["source"]) == "build()"]
|
|
|
|
|
|
def test_a_function_beside_a_same_key_class_resolves_by_its_exact_name(tmp_path: Path):
|
|
calls = _calls(tmp_path, "import core_schema\n\n\ndef build():\n return core_schema.dataclass_schema(1)\n")
|
|
assert calls == [("build()", "dataclass_schema()", "EXTRACTED")]
|
|
|
|
|
|
def test_the_class_itself_resolves_by_its_exact_name(tmp_path: Path):
|
|
calls = _calls(tmp_path, "import core_schema\n\n\ndef build():\n return core_schema.DataclassSchema(type='x')\n")
|
|
assert calls == [("build()", "DataclassSchema", "EXTRACTED")]
|
|
|
|
|
|
def test_an_aliased_module_import_resolves_the_same_way(tmp_path: Path):
|
|
calls = _calls(tmp_path, "import core_schema as cs\n\n\ndef build():\n return cs.dataclass_schema(1)\n")
|
|
assert calls == [("build()", "dataclass_schema()", "EXTRACTED")]
|
|
|
|
|
|
def test_a_name_that_matches_only_after_folding_still_gets_no_edge(tmp_path: Path):
|
|
"""`computevalue` is neither `compute_value` nor `ComputeValue`: fail closed."""
|
|
assert _calls(tmp_path, "import core_schema\n\n\ndef build():\n return core_schema.computevalue()\n") == []
|
|
|
|
|
|
def test_a_call_never_binds_to_an_unrelated_modules_child(tmp_path: Path):
|
|
"""The receiver's own import decides the module. A same-named child of a
|
|
DIFFERENT corpus module must never be bound: the exact-name lookup only
|
|
re-buckets within the module the receiver already resolved to."""
|
|
(tmp_path / "core_schema.py").write_text(_CORE, encoding="utf-8")
|
|
(tmp_path / "other.py").write_text("def unrelated():\n return 0\n", encoding="utf-8")
|
|
(tmp_path / "caller.py").write_text(
|
|
"import other\n\n\ndef build():\n return other.dataclass_schema(1)\n",
|
|
encoding="utf-8")
|
|
result = extract(
|
|
[tmp_path / "caller.py", tmp_path / "core_schema.py", tmp_path / "other.py"],
|
|
root=tmp_path, cache_root=tmp_path / "graphify-out", parallel=False)
|
|
label = {n["id"]: n["label"] for n in result["nodes"]}
|
|
targets = [label.get(e["target"], "") for e in result["edges"]
|
|
if e["relation"] == "calls" and label.get(e["source"]) == "build()"]
|
|
# `other` has no dataclass_schema; core_schema's must NOT be hijacked.
|
|
assert "dataclass_schema()" not in targets, targets
|