-
-
Notifications
You must be signed in to change notification settings - Fork 34.4k
gh-69605: PyREPL: insert "import" after "from foo <tab>" #148445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1168,13 +1168,18 @@ def test_completions(self): | |||||||||||||
| ("import importlib.resources.\t\ta\t\n", "import importlib.resources.abc"), | ||||||||||||||
| ("import foo, impo\t\n", "import foo, importlib"), | ||||||||||||||
| ("import foo as bar, impo\t\n", "import foo as bar, importlib"), | ||||||||||||||
| ("from impo\t\n", "from importlib"), | ||||||||||||||
| ("from importlib.res\t\n", "from importlib.resources"), | ||||||||||||||
| ("from importlib.\t\tres\t\n", "from importlib.resources"), | ||||||||||||||
| ("from importlib.resources.ab\t\n", "from importlib.resources.abc"), | ||||||||||||||
| ("from impo\t\n", "from importlib "), | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure whether we should change this, what do you think?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm yes, I didn't thought of that! But that applies to a lot of modules (including my example of I think having a different behaviour if the module has submodules or not would be both obscure and hard to implement. So I see
eg. to come back to my table:
That allow
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hard probably, but UX-wise I think it'd be nice if it added the space for modules that don't have submodules. It would save you one keystroke and immediately signal that there are no submodules to import from. |
||||||||||||||
| ("from impo\t\t\n", "from importlib import "), | ||||||||||||||
| ("from impo \t\n", "from impo import "), | ||||||||||||||
| ("from importlib.res\t\n", "from importlib.resources "), | ||||||||||||||
| ("from importlib.\t\tres\t\n", "from importlib.resources "), | ||||||||||||||
| ("from importlib.res\t\t\n", "from importlib.resources import "), | ||||||||||||||
| ("from importlib.res \t\n", "from importlib.res import "), | ||||||||||||||
| ("from importlib.resources.ab\t\n", "from importlib.resources.abc "), | ||||||||||||||
| ("from importlib import mac\t\n", "from importlib import machinery"), | ||||||||||||||
| ("from importlib import res\t\n", "from importlib import resources"), | ||||||||||||||
| ("from importlib.res\t import a\t\n", "from importlib.resources import abc"), | ||||||||||||||
| ("from importlib.res\timport a\t\n", "from importlib.resources import abc"), | ||||||||||||||
| ("from importlib.res\t\ta\t\n", "from importlib.resources import abc"), | ||||||||||||||
| ("from __phello__ import s\t\n", "from __phello__ import spam"), # frozen module | ||||||||||||||
| ) | ||||||||||||||
| for code, expected in cases: | ||||||||||||||
|
|
@@ -1191,10 +1196,10 @@ def test_private_completions(self): | |||||||||||||
| cases = ( | ||||||||||||||
| # Return public methods by default | ||||||||||||||
| ("import \t\n", "import public"), | ||||||||||||||
| ("from \t\n", "from public"), | ||||||||||||||
| ("from \t\n", "from public "), | ||||||||||||||
| # Return private methods if explicitly specified | ||||||||||||||
| ("import _\t\n", "import _private"), | ||||||||||||||
| ("from _\t\n", "from _private"), | ||||||||||||||
| ("from _\t\n", "from _private "), | ||||||||||||||
| ) | ||||||||||||||
| for code, expected in cases: | ||||||||||||||
| with self.subTest(code=code): | ||||||||||||||
|
|
@@ -1227,7 +1232,7 @@ def test_sub_module_private_completions(self): | |||||||||||||
| def test_builtin_completion_top_level(self): | ||||||||||||||
| cases = ( | ||||||||||||||
| ("import bui\t\n", "import builtins"), | ||||||||||||||
| ("from bui\t\n", "from builtins"), | ||||||||||||||
| ("from bui\t\n", "from builtins "), | ||||||||||||||
| ) | ||||||||||||||
| for code, expected in cases: | ||||||||||||||
| with self.subTest(code=code): | ||||||||||||||
|
|
@@ -1240,11 +1245,11 @@ def test_relative_completions(self): | |||||||||||||
| cases = ( | ||||||||||||||
| (None, "from .readl\t\n", "from .readl"), | ||||||||||||||
| (None, "from . import readl\t\n", "from . import readl"), | ||||||||||||||
| ("_pyrepl", "from .readl\t\n", "from .readline"), | ||||||||||||||
| ("_pyrepl", "from .readl\t\n", "from .readline "), | ||||||||||||||
| ("_pyrepl", "from . import readl\t\n", "from . import readline"), | ||||||||||||||
| ("_pyrepl", "from .readline import mul\t\n", "from .readline import multiline_input"), | ||||||||||||||
| ("_pyrepl", "from .. import toodeep\t\n", "from .. import toodeep"), | ||||||||||||||
| ("concurrent", "from .futures.i\t\n", "from .futures.interpreter"), | ||||||||||||||
| ("concurrent", "from .futures.i\t\n", "from .futures.interpreter "), | ||||||||||||||
| ) | ||||||||||||||
| for package, code, expected in cases: | ||||||||||||||
| with self.subTest(code=code): | ||||||||||||||
|
|
@@ -1545,41 +1550,43 @@ def test_get_path_and_prefix(self): | |||||||||||||
|
|
||||||||||||||
| def test_parse(self): | ||||||||||||||
| cases = ( | ||||||||||||||
| ('import ', (None, '')), | ||||||||||||||
| ('import foo', (None, 'foo')), | ||||||||||||||
| ('import foo,', (None, '')), | ||||||||||||||
| ('import foo, ', (None, '')), | ||||||||||||||
| ('import foo, bar', (None, 'bar')), | ||||||||||||||
| ('import foo, bar, baz', (None, 'baz')), | ||||||||||||||
| ('import foo as bar,', (None, '')), | ||||||||||||||
| ('import foo as bar, ', (None, '')), | ||||||||||||||
| ('import foo as bar, baz', (None, 'baz')), | ||||||||||||||
| ('import a.', (None, 'a.')), | ||||||||||||||
| ('import a.b', (None, 'a.b')), | ||||||||||||||
| ('import a.b.', (None, 'a.b.')), | ||||||||||||||
| ('import a.b.c', (None, 'a.b.c')), | ||||||||||||||
| ('import a.b.c, foo', (None, 'foo')), | ||||||||||||||
| ('import a.b.c, foo.bar', (None, 'foo.bar')), | ||||||||||||||
| ('import a.b.c, foo.bar,', (None, '')), | ||||||||||||||
| ('import a.b.c, foo.bar, ', (None, '')), | ||||||||||||||
| ('from foo', ('foo', None)), | ||||||||||||||
| ('from a.', ('a.', None)), | ||||||||||||||
| ('from a.b', ('a.b', None)), | ||||||||||||||
| ('from a.b.', ('a.b.', None)), | ||||||||||||||
| ('from a.b.c', ('a.b.c', None)), | ||||||||||||||
| ('from foo import ', ('foo', '')), | ||||||||||||||
| ('from foo import a', ('foo', 'a')), | ||||||||||||||
| ('from ', ('', None)), | ||||||||||||||
| ('from . import a', ('.', 'a')), | ||||||||||||||
| ('from .foo import a', ('.foo', 'a')), | ||||||||||||||
| ('from ..foo import a', ('..foo', 'a')), | ||||||||||||||
| ('from foo import (', ('foo', '')), | ||||||||||||||
| ('from foo import ( ', ('foo', '')), | ||||||||||||||
| ('from foo import (a', ('foo', 'a')), | ||||||||||||||
| ('from foo import (a,', ('foo', '')), | ||||||||||||||
| ('from foo import (a, ', ('foo', '')), | ||||||||||||||
| ('from foo import (a, c', ('foo', 'c')), | ||||||||||||||
| ('from foo import (a as b, c', ('foo', 'c')), | ||||||||||||||
| ('import ', (None, '', True)), | ||||||||||||||
| ('import foo', (None, 'foo', False)), | ||||||||||||||
| ('import foo,', (None, '', False)), | ||||||||||||||
| ('import foo, ', (None, '', True)), | ||||||||||||||
| ('import foo, bar', (None, 'bar', False)), | ||||||||||||||
| ('import foo, bar, baz', (None, 'baz', False)), | ||||||||||||||
| ('import foo as bar,', (None, '', False)), | ||||||||||||||
| ('import foo as bar, ', (None, '', True)), | ||||||||||||||
| ('import foo as bar, baz', (None, 'baz', False)), | ||||||||||||||
| ('import a.', (None, 'a.', False)), | ||||||||||||||
| ('import a.b', (None, 'a.b', False)), | ||||||||||||||
| ('import a.b.', (None, 'a.b.', False)), | ||||||||||||||
| ('import a.b.c', (None, 'a.b.c', False)), | ||||||||||||||
| ('import a.b.c, foo', (None, 'foo', False)), | ||||||||||||||
| ('import a.b.c, foo.bar', (None, 'foo.bar', False)), | ||||||||||||||
| ('import a.b.c, foo.bar,', (None, '', False)), | ||||||||||||||
| ('import a.b.c, foo.bar, ', (None, '', True)), | ||||||||||||||
| ('from foo', ('foo', None, False)), | ||||||||||||||
| ('from foo ', ('foo', None, True)), | ||||||||||||||
| ('from a.', ('a.', None, False)), | ||||||||||||||
| ('from a.b', ('a.b', None, False)), | ||||||||||||||
| ('from a.b.', ('a.b.', None, False)), | ||||||||||||||
| ('from a.b ', ('a.b', None, True)), | ||||||||||||||
| ('from a.b.c', ('a.b.c', None, False)), | ||||||||||||||
| ('from foo import ', ('foo', '', True)), | ||||||||||||||
| ('from foo import a', ('foo', 'a', False)), | ||||||||||||||
| ('from ', ('', None, True)), | ||||||||||||||
| ('from . import a', ('.', 'a', False)), | ||||||||||||||
| ('from .foo import a', ('.foo', 'a', False)), | ||||||||||||||
| ('from ..foo import a', ('..foo', 'a', False)), | ||||||||||||||
| ('from foo import (', ('foo', '', False)), | ||||||||||||||
| ('from foo import ( ', ('foo', '', True)), | ||||||||||||||
| ('from foo import (a', ('foo', 'a', False)), | ||||||||||||||
| ('from foo import (a,', ('foo', '', False)), | ||||||||||||||
| ('from foo import (a, ', ('foo', '', True)), | ||||||||||||||
| ('from foo import (a, c', ('foo', 'c', False)), | ||||||||||||||
| ('from foo import (a as b, c', ('foo', 'c', False)), | ||||||||||||||
| ) | ||||||||||||||
| for code, parsed in cases: | ||||||||||||||
| parser = ImportParser(code) | ||||||||||||||
|
|
@@ -1603,12 +1610,9 @@ def test_parse_error(self): | |||||||||||||
| cases = ( | ||||||||||||||
| '', | ||||||||||||||
| 'import foo ', | ||||||||||||||
| 'from foo ', | ||||||||||||||
| 'import foo. ', | ||||||||||||||
| 'import foo.bar ', | ||||||||||||||
| 'from foo ', | ||||||||||||||
| 'from foo. ', | ||||||||||||||
| 'from foo.bar ', | ||||||||||||||
| 'from foo import bar ', | ||||||||||||||
| 'from foo import (bar ', | ||||||||||||||
| 'from foo import bar, baz ', | ||||||||||||||
|
|
@@ -1647,9 +1651,9 @@ def test_parse_error(self): | |||||||||||||
| 'if 1:\n pass\n\tpass', # _tokenize TabError -> tokenize TabError | ||||||||||||||
| ) | ||||||||||||||
| for code in cases: | ||||||||||||||
| parser = ImportParser(code) | ||||||||||||||
| actual = parser.parse() | ||||||||||||||
| with self.subTest(code=code): | ||||||||||||||
| parser = ImportParser(code) | ||||||||||||||
| actual = parser.parse() | ||||||||||||||
| self.assertEqual(actual, None) | ||||||||||||||
|
|
||||||||||||||
| @patch.dict(sys.modules) | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Insert ``import`` after ``from x.y.z <tab>`` in the :term:`REPL` | ||
| auto-completion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be
space_end?