Skip to content
Merged
5 changes: 3 additions & 2 deletions Lib/_pyrepl/_module_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ def find_modules(self, path: str, prefix: str) -> list[str]:
def _find_modules(self, path: str, prefix: str) -> list[str]:
if not path:
# Top-level import (e.g. `import foo<tab>`` or `from foo<tab>`)`
return [name for _, name, _ in self.global_cache
if name.startswith(prefix)]
builtin_modules = [name for name in sys.builtin_module_names if name.startswith(prefix)]
third_party_modules = [name for _, name, _ in self.global_cache if name.startswith(prefix)]
return sorted(builtin_modules + third_party_modules)

if path.startswith('.'):
# Convert relative path to absolute path
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_pyrepl/test_pyrepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,26 @@ def test_import_completions(self):
output = reader.readline()
self.assertEqual(output, expected)

def test_builtin_completion_top_level(self):
import importlib
# Make iter_modules() search only the standard library.
# This makes the test more reliable in case there are
# other user packages/scripts on PYTHONPATH which can
# intefere with the completions.
lib_path = os.path.dirname(importlib.__path__[0])
sys.path = [lib_path]

cases = (
("import bui\t\n", "import builtins"),
("from bui\t\n", "from builtins"),
)
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_relative_import_completions(self):
cases = (
("from .readl\t\n", "from .readline"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Updated tab completion on REPL to include builtin modules. Contributed by
Tom Wang, Hunter Young
Loading