|
16 | 16 | TYPE_CHECKING = False |
17 | 17 |
|
18 | 18 | if TYPE_CHECKING: |
| 19 | + from types import ModuleType |
19 | 20 | from typing import Any, Iterable, Iterator, Mapping |
20 | 21 | from .types import CompletionAction |
21 | 22 |
|
|
28 | 29 | "xml.parsers.expat": ["errors", "model"], |
29 | 30 | } |
30 | 31 |
|
| 32 | +AUTO_IMPORT_BLACKLIST = { |
| 33 | + # Standard library modules/submodules that have import side effects |
| 34 | + # and must not be automatically imported to complete attributes |
| 35 | + "antigravity", |
| 36 | + "this", |
| 37 | + "idlelib.idle", |
| 38 | +} |
| 39 | + |
31 | 40 |
|
32 | 41 | def make_default_module_completer() -> ModuleCompleter: |
33 | 42 | # Inside pyrepl, __package__ is set to None by default |
@@ -169,15 +178,7 @@ def _find_attributes(self, path: str, prefix: str) -> tuple[list[str], Completio |
169 | 178 | if not imported_module: |
170 | 179 | if path in self._failed_imports: # Do not propose to import again |
171 | 180 | return [], None |
172 | | - root = path.split(".")[0] |
173 | | - mod_info = next((m for m in self.global_cache if m.name == root), |
174 | | - None) |
175 | | - if mod_info and self._is_stdlib_module(mod_info): |
176 | | - # Stdlib module: auto-import (no risk of dangerous side-effect) |
177 | | - try: |
178 | | - imported_module = importlib.import_module(path) |
179 | | - except Exception: |
180 | | - sys.modules.pop(path, None) # Clean half-imported module |
| 181 | + imported_module = self._maybe_import_module(path) |
181 | 182 | if not imported_module: |
182 | 183 | return [], self._get_import_completion_action(path) |
183 | 184 | try: |
@@ -257,6 +258,21 @@ def global_cache(self) -> list[pkgutil.ModuleInfo]: |
257 | 258 | self._global_cache = list(pkgutil.iter_modules()) |
258 | 259 | return self._global_cache |
259 | 260 |
|
| 261 | + def _maybe_import_module(self, fqname: str) -> ModuleType | None: |
| 262 | + if fqname in AUTO_IMPORT_BLACKLIST or fqname.endswith(".__main__"): |
| 263 | + # Special-cased modules with known import side-effects |
| 264 | + return None |
| 265 | + root = fqname.split(".")[0] |
| 266 | + mod_info = next((m for m in self.global_cache if m.name == root), None) |
| 267 | + if not mod_info or not self._is_stdlib_module(mod_info): |
| 268 | + # Only import stdlib modules (no risk of import side-effects) |
| 269 | + return None |
| 270 | + try: |
| 271 | + return importlib.import_module(fqname) |
| 272 | + except Exception: |
| 273 | + sys.modules.pop(fqname, None) # Clean half-imported module |
| 274 | + return None |
| 275 | + |
260 | 276 | def _get_import_completion_action(self, path: str) -> CompletionAction: |
261 | 277 | prompt = ("[ module not imported, press again to import it " |
262 | 278 | "and propose attributes ]") |
|
0 commit comments