Problem or use case
affected, explain and shortest_path miss most Python method callers. _resolve_python_member_calls resolves ClassName.method() (#1446) and module.func() (#1883), and #4176 is adding self.attr.method(). A plain local receiver never gets a calls edge, even when the function itself names its class:
from ._models import Request
from ._client import Client
def send(request: Request): # annotated parameter
request.read() # no edge to Request.read
def build():
client = Client() # constructor binding
client.send() # no edge to Client.send
def get(url):
with Client() as client: # context manager on a constructor
return client.get(url) # no edge to Client.get
Measured against jedi on v8 @ f765dcb (jedi goto on every call inside every function, kept when the target is defined in the repo):
| repo |
real calls graphify finds |
obj.method() calls found |
| httpx |
301 / 437 (68.9%) |
0 / 98 |
click (src/click) |
376 / 502 (74.9%) |
2 / 85 |
Proposed solution
Type the receiver from the function's own code, only when it is certain: a parameter annotation (X, "X", Optional[X], typing.Optional[X], X | None), x = X(...) / x: X, or with X(...) as x. Any other binding of the name in the function (factory call, loop, unpacking, a second class, lambda parameter, match capture, ...) drops the type. Resolve with the existing class/method index and the same origin gate as the TS receiver arm (#2553): the class name is unique and the class is defined in, imported into, or in a module imported by the caller's file. Otherwise no edge.
Alternatives considered
Area
Extraction or language support
Are you willing to submit a PR?
Yes, I can work on this
Compatibility / behavior impact
- Adds
INFERRED calls edges only (confidence_score 0.85). No node, id or existing edge changes.
- Raw calls gain
receiver_type (the key Ruby already uses), so the AST cache schema goes 5 -> 6 and old warm caches re-extract.
- graph.json schema, generated skills and MCP output formats unchanged. Incremental updates behave the same (warm == cold).
Additional context
PR ready right after this issue. 0.9.79 (f765dcb), Windows 11, Python 3.12.12, built from source.
Problem or use case
affected,explainandshortest_pathmiss most Python method callers._resolve_python_member_callsresolvesClassName.method()(#1446) andmodule.func()(#1883), and #4176 is addingself.attr.method(). A plain local receiver never gets acallsedge, even when the function itself names its class:Measured against jedi on v8 @ f765dcb (jedi
gotoon every call inside every function, kept when the target is defined in the repo):obj.method()calls foundsrc/click)Proposed solution
Type the receiver from the function's own code, only when it is certain: a parameter annotation (
X,"X",Optional[X],typing.Optional[X],X | None),x = X(...)/x: X, orwith X(...) as x. Any other binding of the name in the function (factory call, loop, unpacking, a second class, lambda parameter, match capture, ...) drops the type. Resolve with the existing class/method index and the same origin gate as the TS receiver arm (#2553): the class name is unique and the class is defined in, imported into, or in a module imported by the caller's file. Otherwise no edge.Alternatives considered
obj.method()by method name alone: rejected, that is the god-node collision the shared pass deliberately skips (Cross-file INFERREDcallsresolution lacks import evidence + ambiguous-candidate handling — short common names blow up god_nodes ranking #543/Cross-filecallsresolution drops EXTRACTED edges when the callee name has multiple definitions (even with unambiguous import evidence) #1219).x = make_client()): bigger, needs cross-file return types; left out.Area
Extraction or language support
Are you willing to submit a PR?
Yes, I can work on this
Compatibility / behavior impact
INFERREDcallsedges only (confidence_score 0.85). No node, id or existing edge changes.receiver_type(the key Ruby already uses), so the AST cache schema goes 5 -> 6 and old warm caches re-extract.Additional context
PR ready right after this issue. 0.9.79 (f765dcb), Windows 11, Python 3.12.12, built from source.