* Find the file node for a Python dotted module path `a.b.c` — a module file * ending in `a/b/c.py`, or a package `a/b/c/__init__.py` (suffix-matched, so a * package rooted under `src/` etc. still resolves). Returns null for * stdlib/external modules (no matching repo file node), so `import os` c
( mod: string, context: ResolutionContext, excludeFilePath: string )
| 1516 | * (where `c` is a submodule) resolution. |
| 1517 | */ |
| 1518 | function findPythonModuleFile( |
| 1519 | mod: string, |
| 1520 | context: ResolutionContext, |
| 1521 | excludeFilePath: string |
| 1522 | ): Node | null { |
| 1523 | if (!mod || mod.startsWith('.')) return null; // relative imports handled elsewhere |
| 1524 | const rel = mod.replace(/\./g, '/'); |
| 1525 | const lastSeg = mod.split('.').pop()!; |
| 1526 | const endsWith = (p: string, want: string): boolean => p === want || p.endsWith('/' + want); |
| 1527 | const moduleFile = context |
| 1528 | .getNodesByName(`${lastSeg}.py`) |
| 1529 | .find((n) => n.kind === 'file' && n.filePath !== excludeFilePath && endsWith(n.filePath, `${rel}.py`)); |
| 1530 | if (moduleFile) return moduleFile; |
| 1531 | const pkgFile = context |
| 1532 | .getNodesByName('__init__.py') |
| 1533 | .find((n) => n.kind === 'file' && n.filePath !== excludeFilePath && endsWith(n.filePath, `${rel}/__init__.py`)); |
| 1534 | return pkgFile ?? null; |
| 1535 | } |
| 1536 | |
| 1537 | /** |
| 1538 | * Resolve a Python ABSOLUTE dotted module import (`import a.b.c`) to its file — |
no test coverage detected