Skip to content

[Feature]: Python: obj.method() on an annotated parameter or a constructor-bound local produces no calls edge #4197

Description

@rohit-jsfreaky

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.

Activity

  1. github-actions commented on Oct 7, 2026

    @github-actions

    Thanks for opening this issue, @rohit-jsfreaky. A maintainer will take a look soon.

    If you would like to discuss it in real time, come say hi on our Discord server. For longer-form questions and ideas there is also GitHub Discussions.

    To help us triage, please make sure the report includes what you expected, what actually happened, and the steps (and a small sample) to reproduce it.

  2. safishamsi commented on Oct 9, 2026

    @safishamsi
    Member

    Fixed by #4198 (thanks @rohit-jsfreaky), shipped in v0.9.80. Closing as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions