mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
gh-134215: PyREPL: Do not show underscored modules by default during autocompletion (gh-134267)
Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Co-authored-by: Tomas R. <tomas.roun8@gmail.com> Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
parent
c91ad5da9d
commit
a3a3cf6d15
4 changed files with 57 additions and 5 deletions
|
@ -81,8 +81,10 @@ class ModuleCompleter:
|
||||||
def _find_modules(self, path: str, prefix: str) -> list[str]:
|
def _find_modules(self, path: str, prefix: str) -> list[str]:
|
||||||
if not path:
|
if not path:
|
||||||
# Top-level import (e.g. `import foo<tab>`` or `from foo<tab>`)`
|
# Top-level import (e.g. `import foo<tab>`` or `from foo<tab>`)`
|
||||||
builtin_modules = [name for name in sys.builtin_module_names if name.startswith(prefix)]
|
builtin_modules = [name for name in sys.builtin_module_names
|
||||||
third_party_modules = [name for _, name, _ in self.global_cache if name.startswith(prefix)]
|
if self.is_suggestion_match(name, prefix)]
|
||||||
|
third_party_modules = [module.name for module in self.global_cache
|
||||||
|
if self.is_suggestion_match(module.name, prefix)]
|
||||||
return sorted(builtin_modules + third_party_modules)
|
return sorted(builtin_modules + third_party_modules)
|
||||||
|
|
||||||
if path.startswith('.'):
|
if path.startswith('.'):
|
||||||
|
@ -98,7 +100,14 @@ class ModuleCompleter:
|
||||||
if mod_info.ispkg and mod_info.name == segment]
|
if mod_info.ispkg and mod_info.name == segment]
|
||||||
modules = self.iter_submodules(modules)
|
modules = self.iter_submodules(modules)
|
||||||
return [module.name for module in modules
|
return [module.name for module in modules
|
||||||
if module.name.startswith(prefix)]
|
if self.is_suggestion_match(module.name, prefix)]
|
||||||
|
|
||||||
|
def is_suggestion_match(self, module_name: str, prefix: str) -> bool:
|
||||||
|
if prefix:
|
||||||
|
return module_name.startswith(prefix)
|
||||||
|
# For consistency with attribute completion, which
|
||||||
|
# does not suggest private attributes unless requested.
|
||||||
|
return not module_name.startswith("_")
|
||||||
|
|
||||||
def iter_submodules(self, parent_modules: list[pkgutil.ModuleInfo]) -> Iterator[pkgutil.ModuleInfo]:
|
def iter_submodules(self, parent_modules: list[pkgutil.ModuleInfo]) -> Iterator[pkgutil.ModuleInfo]:
|
||||||
"""Iterate over all submodules of the given parent modules."""
|
"""Iterate over all submodules of the given parent modules."""
|
||||||
|
|
|
@ -8,6 +8,7 @@ import select
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from pkgutil import ModuleInfo
|
||||||
from unittest import TestCase, skipUnless, skipIf
|
from unittest import TestCase, skipUnless, skipIf
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from test.support import force_not_colorized, make_clean_env, Py_DEBUG
|
from test.support import force_not_colorized, make_clean_env, Py_DEBUG
|
||||||
|
@ -959,6 +960,46 @@ class TestPyReplModuleCompleter(TestCase):
|
||||||
output = reader.readline()
|
output = reader.readline()
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
@patch("pkgutil.iter_modules", lambda: [ModuleInfo(None, "public", True),
|
||||||
|
ModuleInfo(None, "_private", True)])
|
||||||
|
@patch("sys.builtin_module_names", ())
|
||||||
|
def test_private_completions(self):
|
||||||
|
cases = (
|
||||||
|
# Return public methods by default
|
||||||
|
("import \t\n", "import public"),
|
||||||
|
("from \t\n", "from public"),
|
||||||
|
# Return private methods if explicitly specified
|
||||||
|
("import _\t\n", "import _private"),
|
||||||
|
("from _\t\n", "from _private"),
|
||||||
|
)
|
||||||
|
for code, expected in cases:
|
||||||
|
with self.subTest(code=code):
|
||||||
|
events = code_to_events(code)
|
||||||
|
reader = self.prepare_reader(events, namespace={})
|
||||||
|
output = reader.readline()
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
|
@patch(
|
||||||
|
"_pyrepl._module_completer.ModuleCompleter.iter_submodules",
|
||||||
|
lambda *_: [
|
||||||
|
ModuleInfo(None, "public", True),
|
||||||
|
ModuleInfo(None, "_private", True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_sub_module_private_completions(self):
|
||||||
|
cases = (
|
||||||
|
# Return public methods by default
|
||||||
|
("from foo import \t\n", "from foo import public"),
|
||||||
|
# Return private methods if explicitly specified
|
||||||
|
("from foo import _\t\n", "from foo import _private"),
|
||||||
|
)
|
||||||
|
for code, expected in cases:
|
||||||
|
with self.subTest(code=code):
|
||||||
|
events = code_to_events(code)
|
||||||
|
reader = self.prepare_reader(events, namespace={})
|
||||||
|
output = reader.readline()
|
||||||
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
def test_builtin_completion_top_level(self):
|
def test_builtin_completion_top_level(self):
|
||||||
import importlib
|
import importlib
|
||||||
# Make iter_modules() search only the standard library.
|
# Make iter_modules() search only the standard library.
|
||||||
|
@ -991,8 +1032,8 @@ class TestPyReplModuleCompleter(TestCase):
|
||||||
output = reader.readline()
|
output = reader.readline()
|
||||||
self.assertEqual(output, expected)
|
self.assertEqual(output, expected)
|
||||||
|
|
||||||
@patch("pkgutil.iter_modules", lambda: [(None, 'valid_name', None),
|
@patch("pkgutil.iter_modules", lambda: [ModuleInfo(None, "valid_name", True),
|
||||||
(None, 'invalid-name', None)])
|
ModuleInfo(None, "invalid-name", True)])
|
||||||
def test_invalid_identifiers(self):
|
def test_invalid_identifiers(self):
|
||||||
# Make sure modules which are not valid identifiers
|
# Make sure modules which are not valid identifiers
|
||||||
# are not suggested as those cannot be imported via 'import'.
|
# are not suggested as those cannot be imported via 'import'.
|
||||||
|
|
|
@ -763,6 +763,7 @@ Chris Herborth
|
||||||
Ivan Herman
|
Ivan Herman
|
||||||
Jürgen Hermann
|
Jürgen Hermann
|
||||||
Joshua Jay Herman
|
Joshua Jay Herman
|
||||||
|
Kevin Hernandez
|
||||||
Gary Herron
|
Gary Herron
|
||||||
Ernie Hershey
|
Ernie Hershey
|
||||||
Thomas Herve
|
Thomas Herve
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
:term:`REPL` import autocomplete only suggests private modules when explicitly specified.
|
Loading…
Add table
Add a link
Reference in a new issue