fix(python): resolve call edges through imported modules (#578) (#715)

Give resolvePythonModuleMember the same absolute-dotted-path fallback that
resolveModuleImportToFile already uses, so a `module.func()` call after
`from pkg import module` / `import pkg.module as module` records its `calls`
edge. Adds a regression test and a CHANGELOG entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-07 02:11:06 -04:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 471084dd6e
commit 8d35931c3b
3 changed files with 62 additions and 1 deletions
+11 -1
View File
@@ -1277,7 +1277,17 @@ function resolvePythonModuleMember(
? imp.source + imp.localName
: imp.source + '.' + imp.localName;
const resolvedPath = resolveImportPath(modulePath, ref.filePath, ref.language, context);
// resolveImportPath only maps RELATIVE dotted paths (`.mod`, `..pkg.mod`); an
// ABSOLUTE package path (`pkg.module` from `from pkg import module`, or a bare
// `import pkg.mod`) resolves to null there, so fall back to the dotted-module
// file lookup — the same asymmetry resolveModuleImportToFile already handles
// for the file→file import edge. Without this, a `module.func()` call after
// `from pkg import module` dropped its `calls` edge even though the import
// edge resolved (#578).
let resolvedPath = resolveImportPath(modulePath, ref.filePath, ref.language, context);
if (!resolvedPath) {
resolvedPath = findPythonModuleFile(modulePath, context, ref.filePath)?.filePath ?? null;
}
if (!resolvedPath || resolvedPath === ref.filePath) continue;
// Find the member as a top-level definition in the module file. Exclude