mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
[3.14] gh-69605: Hardcode some stdlib submodules in PyREPL module completion (os.path, collections.abc...) (GH-138268) (GH-138943)
(cherry picked from commit 537133d2b6)
Co-authored-by: Loïc Simon <loic.simon@napta.io>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
parent
cde02ae782
commit
d912e9a852
3 changed files with 80 additions and 14 deletions
|
|
@ -1,9 +1,12 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import pkgutil
|
||||
import sys
|
||||
import token
|
||||
import tokenize
|
||||
from importlib.machinery import FileFinder
|
||||
from io import StringIO
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
|
|
@ -16,6 +19,15 @@
|
|||
from typing import Any, Iterable, Iterator, Mapping
|
||||
|
||||
|
||||
HARDCODED_SUBMODULES = {
|
||||
# Standard library submodules that are not detected by pkgutil.iter_modules
|
||||
# but can be imported, so should be proposed in completion
|
||||
"collections": ["abc"],
|
||||
"os": ["path"],
|
||||
"xml.parsers.expat": ["errors", "model"],
|
||||
}
|
||||
|
||||
|
||||
def make_default_module_completer() -> ModuleCompleter:
|
||||
# Inside pyrepl, __package__ is set to None by default
|
||||
return ModuleCompleter(namespace={'__package__': None})
|
||||
|
|
@ -41,6 +53,7 @@ def __init__(self, namespace: Mapping[str, Any] | None = None) -> None:
|
|||
self.namespace = namespace or {}
|
||||
self._global_cache: list[pkgutil.ModuleInfo] = []
|
||||
self._curr_sys_path: list[str] = sys.path[:]
|
||||
self._stdlib_path = os.path.dirname(importlib.__path__[0])
|
||||
|
||||
def get_completions(self, line: str) -> list[str] | None:
|
||||
"""Return the next possible import completions for 'line'."""
|
||||
|
|
@ -95,12 +108,26 @@ def _find_modules(self, path: str, prefix: str) -> list[str]:
|
|||
return []
|
||||
|
||||
modules: Iterable[pkgutil.ModuleInfo] = self.global_cache
|
||||
is_stdlib_import: bool | None = None
|
||||
for segment in path.split('.'):
|
||||
modules = [mod_info for mod_info in modules
|
||||
if mod_info.ispkg and mod_info.name == segment]
|
||||
if is_stdlib_import is None:
|
||||
# Top-level import decide if we import from stdlib or not
|
||||
is_stdlib_import = all(
|
||||
self._is_stdlib_module(mod_info) for mod_info in modules
|
||||
)
|
||||
modules = self.iter_submodules(modules)
|
||||
return [module.name for module in modules
|
||||
if self.is_suggestion_match(module.name, prefix)]
|
||||
|
||||
module_names = [module.name for module in modules]
|
||||
if is_stdlib_import:
|
||||
module_names.extend(HARDCODED_SUBMODULES.get(path, ()))
|
||||
return [module_name for module_name in module_names
|
||||
if self.is_suggestion_match(module_name, prefix)]
|
||||
|
||||
def _is_stdlib_module(self, module_info: pkgutil.ModuleInfo) -> bool:
|
||||
return (isinstance(module_info.module_finder, FileFinder)
|
||||
and module_info.module_finder.path == self._stdlib_path)
|
||||
|
||||
def is_suggestion_match(self, module_name: str, prefix: str) -> bool:
|
||||
if prefix:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue