mirror of
https://github.com/safishamsi/graphify.git
synced 2026-07-13 02:47:00 +00:00
fix: resolve_seed matches bare names against ()-decorated callable labels
Callable nodes are labeled with a trailing "()" (e.g. classifyProperty()), so a bare-name query like "classifyProperty" falls through the exact-label pass and then ties with any prefix sibling (classifyPropertySafe()) in the contains pass, returning "No unique node match" even though exactly one callable with that name exists. Add a bare-name pass between exact-label and exact-source matching that compares query and label with the trailing "()" stripped from each. Bare queries now resolve decorated labels and vice versa; genuine duplicates still return None. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,12 @@ def _format_location(data: dict) -> str:
|
||||
return str(source_file)
|
||||
|
||||
|
||||
def _bare_name(label: str) -> str:
|
||||
"""Lowercased label with the callable decoration (trailing "()") removed."""
|
||||
label = label.lower()
|
||||
return label[:-2] if label.endswith("()") else label
|
||||
|
||||
|
||||
def resolve_seed(graph: nx.Graph, query: str) -> str | None:
|
||||
if query in graph:
|
||||
return query
|
||||
@@ -54,6 +60,17 @@ def resolve_seed(graph: nx.Graph, query: str) -> str | None:
|
||||
]
|
||||
if len(exact_label_matches) == 1:
|
||||
return exact_label_matches[0]
|
||||
# Callable labels are decorated ("name()"), so a bare "name" query falls
|
||||
# through exact matching and then ties with any "name*" sibling in the
|
||||
# contains pass. Match on the undecorated name before giving up.
|
||||
query_bare = _bare_name(query_lower)
|
||||
bare_name_matches = [
|
||||
str(node_id)
|
||||
for node_id, data in graph.nodes(data=True)
|
||||
if _bare_name(str(data.get("label", ""))) == query_bare
|
||||
]
|
||||
if len(bare_name_matches) == 1:
|
||||
return bare_name_matches[0]
|
||||
exact_source_matches = [
|
||||
str(node_id)
|
||||
for node_id, data in graph.nodes(data=True)
|
||||
|
||||
@@ -92,3 +92,34 @@ def test_affected_cli_forces_directed_on_undirected_graph(monkeypatch, tmp_path,
|
||||
assert "calls" in out
|
||||
# B is the query node, not an affected node, and the result is not empty.
|
||||
assert "No affected nodes found." not in out
|
||||
|
||||
|
||||
def test_resolve_seed_bare_name_matches_callable_label():
|
||||
from graphify.affected import resolve_seed
|
||||
|
||||
graph = nx.DiGraph()
|
||||
graph.add_node("a", label="classifyProperty()", source_file="pkg/entity.py")
|
||||
graph.add_node("b", label="classifyPropertySafe()", source_file="app/context.py")
|
||||
|
||||
assert resolve_seed(graph, "classifyProperty") == "a"
|
||||
assert resolve_seed(graph, "classifyPropertySafe") == "b"
|
||||
|
||||
|
||||
def test_resolve_seed_decorated_query_matches_bare_label():
|
||||
from graphify.affected import resolve_seed
|
||||
|
||||
graph = nx.DiGraph()
|
||||
graph.add_node("a", label="Foo", source_file="pkg/foo.py")
|
||||
graph.add_node("b", label="FooBar", source_file="pkg/foobar.py")
|
||||
|
||||
assert resolve_seed(graph, "Foo()") == "a"
|
||||
|
||||
|
||||
def test_resolve_seed_bare_name_tie_still_returns_none():
|
||||
from graphify.affected import resolve_seed
|
||||
|
||||
graph = nx.DiGraph()
|
||||
graph.add_node("a", label="dup()", source_file="pkg/one.py")
|
||||
graph.add_node("b", label="dup()", source_file="pkg/two.py")
|
||||
|
||||
assert resolve_seed(graph, "dup") is None
|
||||
|
||||
Reference in New Issue
Block a user