From 2ce6850a8f8d90956dadb23898fc32e782eb05b7 Mon Sep 17 00:00:00 2001 From: David Seddon Date: Fri, 7 Aug 2026 11:05:53 +0100 Subject: [PATCH] Fix panic error when scanning imports of nonexistent namespace children --- CHANGELOG.rst | 6 ++++ rust/src/import_scanning.rs | 6 ++++ tests/unit/application/test_scanning.py | 45 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f504cef8..b79eba5a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog ========= +latest +------ + +* Fix panic error when scanning imports of nonexistent namespace children + (https://github.com/python-grimp/grimp/issues/308). + 3.15 (2026-07-03) ----------------- diff --git a/rust/src/import_scanning.rs b/rust/src/import_scanning.rs index 811b7abf..f03ea81d 100644 --- a/rust/src/import_scanning.rs +++ b/rust/src/import_scanning.rs @@ -330,6 +330,12 @@ fn _distill_external_module( while external_path_components[0] == internal_path_components[0] { external_namespace_components.push(external_path_components.remove(0)); internal_path_components.remove(0); + if internal_path_components.is_empty() { + // The module name is a descendant of the found package, but isn't present + // as a module file. If that's the case, don't add it to the graph, as it's + // a missing module. + return None; + }; } external_namespace_components.push(external_path_components[0]); diff --git a/tests/unit/application/test_scanning.py b/tests/unit/application/test_scanning.py index 28289220..bdd55516 100644 --- a/tests/unit/application/test_scanning.py +++ b/tests/unit/application/test_scanning.py @@ -971,6 +971,51 @@ def test_t_string_syntax(): } +@pytest.mark.parametrize( + "import_statement", + ( + "from namespace.portion.nonexistent import something", + "from .nonexistent import something", + "from ..nonexistent import something", + ), +) +def test_missing_module_in_dotted_root_package_is_ignored(import_statement: str): + """ + When a dotted root package (namespace package) contains an import of a missing module + within that same package, and include_external_packages=True, the Rust code should not + panic with an index-out-of-bounds error. + + Regression test for https://github.com/python-grimp/grimp/issues/308. + """ + module_to_scan = Module("namespace.portion.subpackage.importer") + module_file_to_scan = _module_to_module_file(module_to_scan) + all_modules = { + Module("namespace.portion"), + Module("namespace.portion.subpackage"), + module_to_scan, + } + file_system = rust.FakeBasicFileSystem( + content_map={"/path/to/namespace/portion/subpackage/importer.py": import_statement}, + ) + found_packages = { + FoundPackage( + name="namespace.portion", + directory="/path/to/namespace/portion", + module_files=_modules_to_module_files(all_modules), + ) + } + + with override_settings(FILE_SYSTEM=file_system): + result = scanning.scan_imports( + {module_file_to_scan}, + found_packages=found_packages, + include_external_packages=True, + exclude_type_checking_imports=False, # Required field, not relevant. + ) + + assert result == {module_file_to_scan: set()} + + def _module_to_module_file(module: Module) -> ModuleFile: some_mtime = 100933.4 return ModuleFile(module=module, mtime=some_mtime)