diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a2dc0cc0..8fa900e16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # **Upcoming release** -- Remove site-packages from packages search tree (@tkrabel) +- #733 skip directories with perm error when building autoimport index (@MrBago) +- #722, #723 Remove site-packages from packages search tree (@tkrabel) # Release 1.11.0 diff --git a/rope/contrib/autoimport/sqlite.py b/rope/contrib/autoimport/sqlite.py index f02d426ba..1755f2f2b 100644 --- a/rope/contrib/autoimport/sqlite.py +++ b/rope/contrib/autoimport/sqlite.py @@ -546,13 +546,23 @@ def filter_folders(folder: Path) -> bool: folder_paths = filter(filter_folders, folder_paths) # type:ignore return list(OrderedDict.fromkeys(folder_paths)) + def _safe_iterdir(self, folder: Path): + dirs = folder.iterdir() + while True: + try: + yield next(dirs) + except PermissionError: + pass + except StopIteration: + break + def _get_available_packages(self) -> List[Package]: packages: List[Package] = [ Package(module, Source.BUILTIN, None, PackageType.BUILTIN) for module in sys.builtin_module_names ] for folder in self._get_python_folders(): - for package in folder.iterdir(): + for package in self._safe_iterdir(folder): package_tuple = get_package_tuple(package, self.project) if package_tuple is None: continue @@ -602,7 +612,7 @@ def _find_package_path(self, target_name: str) -> Optional[Package]: if target_name in sys.builtin_module_names: return Package(target_name, Source.BUILTIN, None, PackageType.BUILTIN) for folder in self._get_python_folders(): - for package in folder.iterdir(): + for package in self._safe_iterdir(folder): package_tuple = get_package_tuple(package, self.project) if package_tuple is None: continue diff --git a/ropetest/contrib/autoimporttest.py b/ropetest/contrib/autoimporttest.py index 62b6db293..94bb033bb 100644 --- a/ropetest/contrib/autoimporttest.py +++ b/ropetest/contrib/autoimporttest.py @@ -134,13 +134,28 @@ def test_search(self): self.assertIn(import_statement, self.importer.search("D")) def test_generate_full_cache(self): - """The single thread test takes much longer than the multithread test but is easier to debug""" + # The single thread test takes much longer than the multithread test but is easier to debug single_thread = False self.importer.generate_modules_cache(single_thread=single_thread) self.assertIn(("from typing import Dict", "Dict"), self.importer.search("Dict")) - self.assertTrue(len(self.importer._dump_all()) > 0) + self.assertGreater(len(self.importer._dump_all()), 0) for table in self.importer._dump_all(): - self.assertTrue(len(table) > 0) + self.assertGreater(len(table), 0) + + def test_skipping_directories_not_accessible_because_of_permission_error(self): + # The single thread test takes much longer than the multithread test but is easier to debug + single_thread = False + self.importer.generate_modules_cache(single_thread=single_thread) + + # Create a temporary directory and set permissions to 000 + import tempfile, sys + with tempfile.TemporaryDirectory() as dir: + import os + os.chmod(dir, 0o000) + self.importer.project.prefs.python_path = [dir] + self.importer.generate_modules_cache(single_thread=single_thread) + self.assertIn(("from typing import Dict", "Dict"), self.importer.search("Dict")) + self.assertGreater(len(self.importer._dump_all()), 0) class AutoImportObservingTest(unittest.TestCase):