mirror of
https://github.com/giancarloerra/socraticode.git
synced 2026-07-03 14:05:21 +02:00
Merge pull request #47 from giancarloerra/fix/python-sibling-imports
fix(graph): resolve Python sibling-flat imports in service-style monorepos
This commit is contained in:
@@ -189,6 +189,19 @@ export function resolveImport(
|
||||
);
|
||||
if (inSrc) return inSrc;
|
||||
}
|
||||
|
||||
// Sibling-flat fallback (issue #46). Common in service-style monorepos
|
||||
// where each top-level directory is a runnable Python application root
|
||||
// and `import config` from `service-a/main.py` means
|
||||
// `service-a/config.py` because the file is run via `python main.py`
|
||||
// from inside its own directory. Tried last to preserve existing
|
||||
// project-root precedence: any layout that already resolved before
|
||||
// this PR continues to resolve to the same file. resolveRelativePath
|
||||
// also handles the `<sourceDir>/<module>/__init__.py` package case
|
||||
// via its built-in Python init fallback.
|
||||
const sibling = resolveRelativePath(modulePath, sourceDir, projectPath, fileSet, [".py"]);
|
||||
if (sibling) return sibling;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -268,6 +268,103 @@ describe("graph-resolution", () => {
|
||||
|
||||
expect(result).toBe("src/mypackage/utils.py");
|
||||
});
|
||||
|
||||
it("resolves sibling-flat imports in service-style monorepos (#46)", () => {
|
||||
// service-a/main.py runs as `python main.py` from inside service-a/,
|
||||
// so `import config` resolves to service-a/config.py at runtime.
|
||||
project = createTempProject({
|
||||
"service-a/main.py": "",
|
||||
"service-a/config.py": "",
|
||||
"service-b/main.py": "",
|
||||
"service-b/config.py": "",
|
||||
});
|
||||
|
||||
const result = resolveImport(
|
||||
"config",
|
||||
path.join(project.root, "service-a/main.py"),
|
||||
project.root,
|
||||
project.fileSet,
|
||||
"python",
|
||||
);
|
||||
|
||||
expect(result).toBe("service-a/config.py");
|
||||
});
|
||||
|
||||
it("resolves sibling-flat imports for dotted module paths", () => {
|
||||
// `import shared.utils` from service-a/main.py with shared/utils.py
|
||||
// sitting next to main.py.
|
||||
project = createTempProject({
|
||||
"service-a/main.py": "",
|
||||
"service-a/shared/utils.py": "",
|
||||
});
|
||||
|
||||
const result = resolveImport(
|
||||
"shared.utils",
|
||||
path.join(project.root, "service-a/main.py"),
|
||||
project.root,
|
||||
project.fileSet,
|
||||
"python",
|
||||
);
|
||||
|
||||
expect(result).toBe("service-a/shared/utils.py");
|
||||
});
|
||||
|
||||
it("resolves sibling packages via __init__.py", () => {
|
||||
// `import config` resolves to service-a/config/__init__.py when no
|
||||
// bare service-a/config.py exists.
|
||||
project = createTempProject({
|
||||
"service-a/main.py": "",
|
||||
"service-a/config/__init__.py": "",
|
||||
});
|
||||
|
||||
const result = resolveImport(
|
||||
"config",
|
||||
path.join(project.root, "service-a/main.py"),
|
||||
project.root,
|
||||
project.fileSet,
|
||||
"python",
|
||||
);
|
||||
|
||||
expect(result).toBe("service-a/config/__init__.py");
|
||||
});
|
||||
|
||||
it("preserves project-root precedence when the same name exists at root and as a sibling", () => {
|
||||
// Backward-compat guarantee: `import config` from service-a/main.py
|
||||
// still resolves to the project-root config.py if one exists, so
|
||||
// existing layouts do not change after this PR. The sibling fallback
|
||||
// only fires when the project-root lookup fails.
|
||||
project = createTempProject({
|
||||
"config.py": "",
|
||||
"service-a/main.py": "",
|
||||
"service-a/config.py": "",
|
||||
});
|
||||
|
||||
const result = resolveImport(
|
||||
"config",
|
||||
path.join(project.root, "service-a/main.py"),
|
||||
project.root,
|
||||
project.fileSet,
|
||||
"python",
|
||||
);
|
||||
|
||||
expect(result).toBe("config.py");
|
||||
});
|
||||
|
||||
it("returns null when neither project-root, src/, lib/, nor sibling have the module", () => {
|
||||
project = createTempProject({
|
||||
"service-a/main.py": "",
|
||||
});
|
||||
|
||||
const result = resolveImport(
|
||||
"config",
|
||||
path.join(project.root, "service-a/main.py"),
|
||||
project.root,
|
||||
project.fileSet,
|
||||
"python",
|
||||
);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Rust resolution", () => {
|
||||
|
||||
Reference in New Issue
Block a user