From 9fd35b18389b46ec5898f773885347d18ad3bb24 Mon Sep 17 00:00:00 2001 From: Deepak Ganesh Date: Sat, 25 Jul 2026 18:23:51 +0530 Subject: [PATCH] Fix autoimport.sqlite deriving module names from non-package folders (#823) `AutoImport._resource_to_module` computed a module's name via `get_modname_from_path(resource_path, project_package.path)`, i.e. the resource's path relative to the project root. This blindly joins every intermediate directory name, so a module under a non-package folder such as `/shared/src/shared/documents.py` was reported as `shared.src.shared.documents` instead of the correct `shared.documents`. The pickle backend does not have this problem because it derives the name with `libutils.modname`, which walks up the package hierarchy and stops at the first directory without an `__init__.py`. Use the same helper in the sqlite backend so both backends agree and intermediate non-package folders are excluded from the module name. Fixes #823 --- CHANGELOG.md | 1 + rope/contrib/autoimport/sqlite.py | 14 +++++++------- ropetest/contrib/autoimport/autoimporttest.py | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef219ac71..7ea5b82a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # **Upcoming release** +- #823 Fix autoimport.sqlite deriving module names from non-package folders (@deepakganesh78) - ... # Release 1.14.0 diff --git a/rope/contrib/autoimport/sqlite.py b/rope/contrib/autoimport/sqlite.py index f06fdaca3..45eccf9eb 100644 --- a/rope/contrib/autoimport/sqlite.py +++ b/rope/contrib/autoimport/sqlite.py @@ -44,7 +44,6 @@ from rope.contrib.autoimport.parse import get_names from rope.contrib.autoimport.utils import ( get_files, - get_modname_from_path, get_package_tuple, sort_and_deduplicate, sort_and_deduplicate_tuple, @@ -654,12 +653,13 @@ def _resource_to_module( assert self.project_package.path underlined = underlined if underlined else self.underlined resource_path: Path = resource.pathlib - # The project doesn't need its name added to the path, - # since the standard python file layout accounts for that - # so we set add_package_name to False - resource_modname: str = get_modname_from_path( - resource_path, self.project_package.path, add_package_name=False - ) + # Resolve the module name by walking up the package hierarchy from the + # resource (stopping at the first directory without an ``__init__.py``), + # rather than deriving it from the resource's path relative to the + # project root. The latter incorrectly includes intermediate, + # non-package directories in the module name. + # See https://github.com/python-rope/rope/issues/823 + resource_modname: str = libutils.modname(resource) return ModuleFile( resource_path, resource_modname, diff --git a/ropetest/contrib/autoimport/autoimporttest.py b/ropetest/contrib/autoimport/autoimporttest.py index 072ab1ed8..d769c825e 100644 --- a/ropetest/contrib/autoimport/autoimporttest.py +++ b/ropetest/contrib/autoimport/autoimporttest.py @@ -132,6 +132,23 @@ def test_connection(project: Project, project2: Project): assert ai1.connection is not ai3.connection +def test_resource_to_module_skips_non_package_folders( + autoimport: AutoImport, + project: Project, +): + # Intermediate folders that are not packages (no ``__init__.py``) must not + # be included in the derived module name. + # Regression test for https://github.com/python-rope/rope/issues/823 + src = project.root.create_folder("src") + pkg = src.create_folder("mypkg") + pkg.create_file("__init__.py") + documents = pkg.create_file("documents.py") + + module = autoimport._resource_to_module(documents) + + assert module.modname == "mypkg.documents" + + @contextmanager def assert_database_is_reset(conn): conn.execute("ALTER TABLE names ADD COLUMN deprecated_column")