|
| 1 | +import importlib |
1 | 2 | import io |
2 | 3 | import itertools |
3 | 4 | import os |
|
26 | 27 | code_to_events, |
27 | 28 | ) |
28 | 29 | from _pyrepl.console import Event |
29 | | -from _pyrepl._module_completer import ImportParser, ModuleCompleter |
| 30 | +from _pyrepl._module_completer import (ImportParser, ModuleCompleter, |
| 31 | + HARDCODED_SUBMODULES) |
30 | 32 | from _pyrepl.readline import (ReadlineAlikeReader, ReadlineConfig, |
31 | 33 | _ReadlineWrapper) |
32 | 34 | from _pyrepl.readline import multiline_input as readline_multiline_input |
@@ -930,7 +932,6 @@ def test_func(self): |
930 | 932 |
|
931 | 933 | class TestPyReplModuleCompleter(TestCase): |
932 | 934 | def setUp(self): |
933 | | - import importlib |
934 | 935 | # Make iter_modules() search only the standard library. |
935 | 936 | # This makes the test more reliable in case there are |
936 | 937 | # other user packages/scripts on PYTHONPATH which can |
@@ -1013,14 +1014,6 @@ def test_sub_module_private_completions(self): |
1013 | 1014 | self.assertEqual(output, expected) |
1014 | 1015 |
|
1015 | 1016 | def test_builtin_completion_top_level(self): |
1016 | | - import importlib |
1017 | | - # Make iter_modules() search only the standard library. |
1018 | | - # This makes the test more reliable in case there are |
1019 | | - # other user packages/scripts on PYTHONPATH which can |
1020 | | - # intefere with the completions. |
1021 | | - lib_path = os.path.dirname(importlib.__path__[0]) |
1022 | | - sys.path = [lib_path] |
1023 | | - |
1024 | 1017 | cases = ( |
1025 | 1018 | ("import bui\t\n", "import builtins"), |
1026 | 1019 | ("from bui\t\n", "from builtins"), |
@@ -1076,6 +1069,20 @@ def test_no_fallback_on_regular_completion(self): |
1076 | 1069 | output = reader.readline() |
1077 | 1070 | self.assertEqual(output, expected) |
1078 | 1071 |
|
| 1072 | + def test_hardcoded_stdlib_submodules(self): |
| 1073 | + cases = ( |
| 1074 | + ("import collections.\t\n", "import collections.abc"), |
| 1075 | + ("from os import \t\n", "from os import path"), |
| 1076 | + ("import xml.parsers.expat.\t\te\t\n\n", "import xml.parsers.expat.errors"), |
| 1077 | + ("from xml.parsers.expat import \t\tm\t\n\n", "from xml.parsers.expat import model"), |
| 1078 | + ) |
| 1079 | + for code, expected in cases: |
| 1080 | + with self.subTest(code=code): |
| 1081 | + events = code_to_events(code) |
| 1082 | + reader = self.prepare_reader(events, namespace={}) |
| 1083 | + output = reader.readline() |
| 1084 | + self.assertEqual(output, expected) |
| 1085 | + |
1079 | 1086 | def test_get_path_and_prefix(self): |
1080 | 1087 | cases = ( |
1081 | 1088 | ('', ('', '')), |
@@ -1204,6 +1211,19 @@ def test_parse_error(self): |
1204 | 1211 | with self.subTest(code=code): |
1205 | 1212 | self.assertEqual(actual, None) |
1206 | 1213 |
|
| 1214 | + |
| 1215 | +class TestHardcodedSubmodules(TestCase): |
| 1216 | + def test_hardcoded_stdlib_submodules_are_importable(self): |
| 1217 | + for parent_path, submodules in HARDCODED_SUBMODULES.items(): |
| 1218 | + for module_name in submodules: |
| 1219 | + path = f"{parent_path}.{module_name}" |
| 1220 | + with self.subTest(path=path): |
| 1221 | + # We can't use importlib.util.find_spec here, |
| 1222 | + # since some hardcoded submodules parents are |
| 1223 | + # not proper packages |
| 1224 | + importlib.import_module(path) |
| 1225 | + |
| 1226 | + |
1207 | 1227 | class TestPasteEvent(TestCase): |
1208 | 1228 | def prepare_reader(self, events): |
1209 | 1229 | console = FakeConsole(events) |
|
0 commit comments