Skip to content

Python: self.x() on an instance attribute links to an unrelated module-level function x #2958

Open

Description

@inth3shadows

self.x(), where x is an instance attribute holding a callable, links by exact name to a module-level function x anywhere in the project, even in an unrelated file.

# pkg/cli.py
class ScriptInfo:
    def __init__(self, create_app=None):
        self.create_app = create_app

    def load_app(self):
        if self.create_app is not None:
            return self.create_app()       # line 7
        return None

# examples/factory.py
def create_app():
    return object()
call edge on main expected
self.create_app() (line 7) create_app in examples/factory.py (resolvedBy: exact-match) ✗ none: it calls whatever was passed in

Reproduced on main at 2577601, with CODEGRAPH_KERNEL=0 and with the native kernel. Linux/WSL2, Node 22.23.2.

Where it shows up. I checked every Python calls edge against jedi's goto-definition and found two cases in flask (d086db8). The first is the core of its CLI: ScriptInfo.load_app in src/flask/cli.py:342 calls self.create_app() (assigned in __init__, line 316), and it links to examples/celery/src/task_app/__init__.py's create_app. So the example app shows up as a callee of Flask's app loading. In the second, self.app(environ, start_response) in tests/test_basic.py:299 links to the app pytest fixture in tests/conftest.py.

Expected. A call with a self./cls. receiver names a member of the instance or class: a method in the class or its bases, or an attribute. It should never fall through to a same-named module-level function in another file. 2577601 made such calls prefer the caller's own class member when one exists; this is the case where none does, and the bare-name match still answers.

Related: 2577601 / #1952, #2954 (TS this.m() landing on a same-named import), #2948.

Activity

  1. inth3shadows commented on Oct 29, 2026

    @inth3shadows
    ContributorAuthor

    Re-checked on main at 60b0d7a (2026-10-29): still reproduces as described.

    Two more shapes, found by diffing codegraph's call edges against scip-python:

    • The target can be a module-level variable, not only a function, in a module the caller never imports. In a PyTorch model, self.out = nn.Linear(h, V) is set in __init__, and forward calls self.out(...). That call links to out = (games.filter(...)), a module-level variable in an unrelated script (labs/nn-nfl-lab/export_features.py:15).
    • The attribute can be bound by tuple unpacking in setUp. A test binds self.repo, self.run = _git_repo(...) and then calls self.run("git", "add", "-A"). The call links to a free function run in a harness module the test doesn't import.

    Minimal form:

    # other/features.py
    def out(x):
        return x
    
    # lab/model.py
    class Layer:
        def __call__(self, x):
            return x
    
    class MLP:
        def __init__(self):
            self.out = Layer()
        def forward(self, x):
            return self.out(x)   # links to other/features.py: out (exact-match)

    I filed this again by mistake as #3039, which is now closed as a duplicate of this issue.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions