PROBLEM
For an intra-class method call (self._method(...)), the emitted call graph points the edge at the CLASS node instead of the callee method — and the callee method may not appear as a graph node at all. The symbol table is correct (the method exists as a PyCallable with the right signature); only the call-site/edge resolution is wrong. This silently truncates call chains at their deepest hop (the codellm-devkit/python-sdk#65 symptom: web_auth_reset_password -> ResUsers.reset_password -> _action_reset_password loses the last hop).
MINIMAL REPRO (verified against installed 0.3.0)
# pkg/mod.pyclassResUsers:
defreset_password(self, login):
returnself._action_reset_password([login])
def_action_reset_password(self, ids):
returnidsdefentry():
returnResUsers().reset_password("x")Raw Codeanalyzer.analyze() output (no SDK involved):
source='pkg.mod.entry' target='pkg.mod.ResUsers.__init__' type='CALL_DEP' weight=2 provenance=['jedi']
source='pkg.mod.ResUsers.reset_password' target='pkg.mod.ResUsers' type='CALL_DEP' weight=1 provenance=['jedi']
Expected second edge target: pkg.mod.ResUsers._action_reset_password.
Raw call-site record for the intra-class call — callee_signature is the class:
{"method_name":"_action_reset_password","receiver_expr":"self","receiver_type":"ResUsers",
"callee_signature":"pkg.mod.ResUsers","is_constructor_call":false,"start_line":3}ROOT CAUSE (localized in 0.3.0)
codeanalyzer/syntactic_analysis/symbol_table_builder.py, _call_sites (~lines 590-594): _infer_callee(script, node.lineno, node.col_offset) is called with the ast.Call node's position. For attribute calls (self.method(...)), node.col_offset is the column of the RECEIVER (self), not the method name — so Jedi infers the receiver's type, whose full_name collapses to the class's qualified name. _infer_callee's constructor-rewrite guard (d.type == "class", ~line 92) does not fire because an instance's d.type is "instance", so the class name passes through unrewritten. Bare-name calls (ResUsers()) work only because the call's col_offset coincides with the callee expression there.
Likely fix direction: for ast.Attribute callees, infer at the attribute NAME's position (node.func.value.end_col_offset + 1 / node.func.attr position), or resolve via the attribute node explicitly.
DOWNSTREAM / PROPAGATION
- The SDK (
python-sdk) is a verbatim pass-through (verified: _build_call_graph and get_callsites_for forward analyzer output unmodified) — no SDK-side fix applies. - python-sdk 1.x is pinned to 0.3.0: fixing this for 1.x users needs a 0.3.1 backport, or ships naturally with 0.4.0 (schema v2) if the v2 emission path inherits the same
_call_sites inference — PLEASE VERIFY the v2 branch against this repro before cutting 0.4.0; the L1/L2 jedi path likely shares the code, and the superset gate would then bake the wrong edge into v2 goldens. - Suggested regression gates: (1) edge
reset_password -> ResUsers._action_reset_password exists; (2) NO callable -> class edge for a non-constructor call; (3) callsite callee_signature for self.x(...) ends with .x.
Reported from a python-sdk audit session; full repro scripts available on request.
PROBLEM
For an intra-class method call (
self._method(...)), the emitted call graph points the edge at the CLASS node instead of the callee method — and the callee method may not appear as a graph node at all. The symbol table is correct (the method exists as a PyCallable with the right signature); only the call-site/edge resolution is wrong. This silently truncates call chains at their deepest hop (the codellm-devkit/python-sdk#65 symptom:web_auth_reset_password -> ResUsers.reset_password -> _action_reset_passwordloses the last hop).MINIMAL REPRO (verified against installed 0.3.0)
Raw
Codeanalyzer.analyze()output (no SDK involved):Expected second edge target:
pkg.mod.ResUsers._action_reset_password.Raw call-site record for the intra-class call —
callee_signatureis the class:{"method_name":"_action_reset_password","receiver_expr":"self","receiver_type":"ResUsers", "callee_signature":"pkg.mod.ResUsers","is_constructor_call":false,"start_line":3}ROOT CAUSE (localized in 0.3.0)
codeanalyzer/syntactic_analysis/symbol_table_builder.py,_call_sites(~lines 590-594):_infer_callee(script, node.lineno, node.col_offset)is called with theast.Callnode's position. For attribute calls (self.method(...)),node.col_offsetis the column of the RECEIVER (self), not the method name — so Jedi infers the receiver's type, whosefull_namecollapses to the class's qualified name._infer_callee's constructor-rewrite guard (d.type == "class", ~line 92) does not fire because an instance'sd.typeis"instance", so the class name passes through unrewritten. Bare-name calls (ResUsers()) work only because the call's col_offset coincides with the callee expression there.Likely fix direction: for
ast.Attributecallees, infer at the attribute NAME's position (node.func.value.end_col_offset + 1/node.func.attrposition), or resolve via the attribute node explicitly.DOWNSTREAM / PROPAGATION
python-sdk) is a verbatim pass-through (verified:_build_call_graphandget_callsites_forforward analyzer output unmodified) — no SDK-side fix applies._call_sitesinference — PLEASE VERIFY the v2 branch against this repro before cutting 0.4.0; the L1/L2 jedi path likely shares the code, and the superset gate would then bake the wrong edge into v2 goldens.reset_password -> ResUsers._action_reset_passwordexists; (2) NOcallable -> classedge for a non-constructor call; (3) callsitecallee_signatureforself.x(...)ends with.x.Reported from a python-sdk audit session; full repro scripts available on request.