From 1374212ded801f6b1a811e03ae9731a9842fae5b Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Tue, 8 Aug 2023 05:52:46 +0900 Subject: [PATCH 01/22] gh-102130: Support a tab completion for Libedit in cmd, site, and pdb. --- Doc/library/cmd.rst | 4 ++-- Doc/library/pdb.rst | 2 +- Doc/library/readline.rst | 36 +++++++++++++++++++++++++++++++++--- Doc/library/rlcompleter.rst | 2 +- Lib/cmd.py | 8 ++++++-- Lib/pdb.py | 2 +- Lib/site.py | 4 ++-- Misc/ACKS | 1 + 8 files changed, 47 insertions(+), 12 deletions(-) diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index fd5df96dfd0b3d..ebfa9f48bc0295 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -15,7 +15,7 @@ command interpreters. These are often useful for test harnesses, administrative tools, and prototypes that will later be wrapped in a more sophisticated interface. -.. class:: Cmd(completekey='tab', stdin=None, stdout=None) +.. class:: Cmd(completekey=r'"\t"', stdin=None, stdout=None) A :class:`Cmd` instance or subclass instance is a line-oriented interpreter framework. There is no good reason to instantiate :class:`Cmd` itself; rather, @@ -23,7 +23,7 @@ interface. to inherit :class:`Cmd`'s methods and encapsulate action methods. The optional argument *completekey* is the :mod:`readline` name of a completion - key; it defaults to :kbd:`Tab`. If *completekey* is not :const:`None` and + key; it defaults to "\\t"(:kbd:`tab`). If *completekey* is not :const:`None` and :mod:`readline` is available, command completion is done automatically. The optional arguments *stdin* and *stdout* specify the input and output file diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index ef52370bff8058..c9665e8ba3b236 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -183,7 +183,7 @@ The ``run*`` functions and :func:`set_trace` are aliases for instantiating the :class:`Pdb` class and calling the method of the same name. If you want to access further features, you have to do this yourself: -.. class:: Pdb(completekey='tab', stdin=None, stdout=None, skip=None, \ +.. class:: Pdb(completekey=r'"\t"', stdin=None, stdout=None, skip=None, \ nosigint=False, readrc=True) :class:`Pdb` is the debugger class. diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst index 8fb0eca8df74d8..f23321070266c6 100644 --- a/Doc/library/readline.rst +++ b/Doc/library/readline.rst @@ -17,6 +17,21 @@ made using this module affect the behaviour of both the interpreter's interactive prompt and the prompts offered by the built-in :func:`input` function. +Compatible keybindings +---------------------- + +To support compatible keybindings between GNU readline and Libedit, use the +following backslashed escape sequences or octal escape sequences in quotes. :: + + \a bell + \b backspace + \f form feed + \n newline + \r carriage return + \t horizontal tab + \v vertical tab + \nnn octal escape sequences + Readline keybindings may be configured via an initialization file, typically ``.inputrc`` in your home directory. See `Readline Init File `_ @@ -39,10 +54,10 @@ Readline library in general. If you use *editline*/``libedit`` readline emulation on macOS, the initialization file located in your home directory is named ``.editrc``. For example, the following content in ``~/.editrc`` will - turn ON *vi* keybindings and TAB completion:: + turn ON *vi* keybindings and "\\t"(:kbd:`tab`) completion:: python:bind -v - python:bind ^I rl_complete + python:bind "\t" rl_complete Init file @@ -56,6 +71,17 @@ The following functions relate to the init file and user configuration: Execute the init line provided in the *string* argument. This calls :c:func:`rl_parse_and_bind` in the underlying library. +.. note:: + The syntax and command of *string* argument may be different depending + on the readline library. + + For GNU readline:: + + readline.parse_and_bind(r'"\t": complete') + + For Libedit:: + + readline.parse_and_bind(r'bind "\t" rl_complete') .. function:: read_init_file([filename]) @@ -348,7 +374,11 @@ support history save/restore. :: self.init_history(histfile) def init_history(self, histfile): - readline.parse_and_bind("tab: complete") + readline_doc = getattr(readline, '__doc__', '') + if readline_doc is not None and 'libedit' in readline_doc: + readline.parse_and_bind(r'bind "\t" rl_complete') + else: + readline.parse_and_bind(r'"\t": complete') if hasattr(readline, "read_history_file"): try: readline.read_history_file(histfile) diff --git a/Doc/library/rlcompleter.rst b/Doc/library/rlcompleter.rst index 40b09ce897880e..fd521cc5de1365 100644 --- a/Doc/library/rlcompleter.rst +++ b/Doc/library/rlcompleter.rst @@ -21,7 +21,7 @@ Example:: >>> import rlcompleter >>> import readline - >>> readline.parse_and_bind("tab: complete") + >>> readline.parse_and_bind(r'"\t": complete') >>> readline. readline.__doc__ readline.get_line_buffer( readline.read_init_file( readline.__file__ readline.insert_text( readline.set_completer( diff --git a/Lib/cmd.py b/Lib/cmd.py index 88ee7d3ddc4694..cd138a66d099cd 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -73,7 +73,7 @@ class Cmd: nohelp = "*** No help on %s" use_rawinput = 1 - def __init__(self, completekey='tab', stdin=None, stdout=None): + def __init__(self, completekey=r'"\t"', stdin=None, stdout=None): """Instantiate a line-oriented interpreter framework. The optional argument 'completekey' is the readline name of a @@ -108,7 +108,11 @@ def cmdloop(self, intro=None): import readline self.old_completer = readline.get_completer() readline.set_completer(self.complete) - readline.parse_and_bind(self.completekey+": complete") + readline_doc = getattr(readline, '__doc__', '') + if readline_doc is not None and 'libedit' in readline_doc: + readline.parse_and_bind("bind "+self.completekey+" rl_complete") + else: + readline.parse_and_bind(self.completekey+": complete") except ImportError: pass try: diff --git a/Lib/pdb.py b/Lib/pdb.py index 3db3e6a5be1a7b..e92e6604803c9b 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -209,7 +209,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): _previous_sigint_handler = None - def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, + def __init__(self, completekey=r'"\t"', stdin=None, stdout=None, skip=None, nosigint=False, readrc=True): bdb.Bdb.__init__(self, skip=skip) cmd.Cmd.__init__(self, completekey, stdin, stdout) diff --git a/Lib/site.py b/Lib/site.py index 672fa7b000ad02..342f9e71b8a519 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -446,9 +446,9 @@ def register_readline(): # completion key, so we set one first and then read the file. readline_doc = getattr(readline, '__doc__', '') if readline_doc is not None and 'libedit' in readline_doc: - readline.parse_and_bind('bind ^I rl_complete') + readline.parse_and_bind(r'bind "\t" rl_complete') else: - readline.parse_and_bind('tab: complete') + readline.parse_and_bind(r'"\t": complete') try: readline.read_init_file() diff --git a/Misc/ACKS b/Misc/ACKS index 8b8c5ad8434bd7..0aea3e42754086 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -784,6 +784,7 @@ Thomas Holmes Craig Holmquist Philip Homburg Naofumi Honda +Constantin Hong Weipeng Hong Jeffrey Honig Rob Hooft From d11ec11a7a688f60531799b68fd1474bd9de5856 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:11:25 +0000 Subject: [PATCH 02/22] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst diff --git a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst new file mode 100644 index 00000000000000..a4215c10596930 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst @@ -0,0 +1 @@ +Support a tab completion for Libedit in :mod:`cmd `, :mod:`site`, and :mod:`pdb`. From a7443e8139a7632ebda933335316b756512507d6 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Tue, 7 Nov 2023 03:13:00 +0900 Subject: [PATCH 03/22] Revert "gh-102130: Support a tab completion for Libedit in cmd, site, and pdb." This reverts commit 1374212ded801f6b1a811e03ae9731a9842fae5b. --- Doc/library/cmd.rst | 4 ++-- Doc/library/pdb.rst | 2 +- Doc/library/readline.rst | 36 +++--------------------------------- Doc/library/rlcompleter.rst | 2 +- Lib/cmd.py | 8 ++------ Lib/pdb.py | 2 +- Lib/site.py | 4 ++-- Misc/ACKS | 1 - 8 files changed, 12 insertions(+), 47 deletions(-) diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index ebfa9f48bc0295..fd5df96dfd0b3d 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -15,7 +15,7 @@ command interpreters. These are often useful for test harnesses, administrative tools, and prototypes that will later be wrapped in a more sophisticated interface. -.. class:: Cmd(completekey=r'"\t"', stdin=None, stdout=None) +.. class:: Cmd(completekey='tab', stdin=None, stdout=None) A :class:`Cmd` instance or subclass instance is a line-oriented interpreter framework. There is no good reason to instantiate :class:`Cmd` itself; rather, @@ -23,7 +23,7 @@ interface. to inherit :class:`Cmd`'s methods and encapsulate action methods. The optional argument *completekey* is the :mod:`readline` name of a completion - key; it defaults to "\\t"(:kbd:`tab`). If *completekey* is not :const:`None` and + key; it defaults to :kbd:`Tab`. If *completekey* is not :const:`None` and :mod:`readline` is available, command completion is done automatically. The optional arguments *stdin* and *stdout* specify the input and output file diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index c9665e8ba3b236..ef52370bff8058 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -183,7 +183,7 @@ The ``run*`` functions and :func:`set_trace` are aliases for instantiating the :class:`Pdb` class and calling the method of the same name. If you want to access further features, you have to do this yourself: -.. class:: Pdb(completekey=r'"\t"', stdin=None, stdout=None, skip=None, \ +.. class:: Pdb(completekey='tab', stdin=None, stdout=None, skip=None, \ nosigint=False, readrc=True) :class:`Pdb` is the debugger class. diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst index f23321070266c6..8fb0eca8df74d8 100644 --- a/Doc/library/readline.rst +++ b/Doc/library/readline.rst @@ -17,21 +17,6 @@ made using this module affect the behaviour of both the interpreter's interactive prompt and the prompts offered by the built-in :func:`input` function. -Compatible keybindings ----------------------- - -To support compatible keybindings between GNU readline and Libedit, use the -following backslashed escape sequences or octal escape sequences in quotes. :: - - \a bell - \b backspace - \f form feed - \n newline - \r carriage return - \t horizontal tab - \v vertical tab - \nnn octal escape sequences - Readline keybindings may be configured via an initialization file, typically ``.inputrc`` in your home directory. See `Readline Init File `_ @@ -54,10 +39,10 @@ Readline library in general. If you use *editline*/``libedit`` readline emulation on macOS, the initialization file located in your home directory is named ``.editrc``. For example, the following content in ``~/.editrc`` will - turn ON *vi* keybindings and "\\t"(:kbd:`tab`) completion:: + turn ON *vi* keybindings and TAB completion:: python:bind -v - python:bind "\t" rl_complete + python:bind ^I rl_complete Init file @@ -71,17 +56,6 @@ The following functions relate to the init file and user configuration: Execute the init line provided in the *string* argument. This calls :c:func:`rl_parse_and_bind` in the underlying library. -.. note:: - The syntax and command of *string* argument may be different depending - on the readline library. - - For GNU readline:: - - readline.parse_and_bind(r'"\t": complete') - - For Libedit:: - - readline.parse_and_bind(r'bind "\t" rl_complete') .. function:: read_init_file([filename]) @@ -374,11 +348,7 @@ support history save/restore. :: self.init_history(histfile) def init_history(self, histfile): - readline_doc = getattr(readline, '__doc__', '') - if readline_doc is not None and 'libedit' in readline_doc: - readline.parse_and_bind(r'bind "\t" rl_complete') - else: - readline.parse_and_bind(r'"\t": complete') + readline.parse_and_bind("tab: complete") if hasattr(readline, "read_history_file"): try: readline.read_history_file(histfile) diff --git a/Doc/library/rlcompleter.rst b/Doc/library/rlcompleter.rst index fd521cc5de1365..40b09ce897880e 100644 --- a/Doc/library/rlcompleter.rst +++ b/Doc/library/rlcompleter.rst @@ -21,7 +21,7 @@ Example:: >>> import rlcompleter >>> import readline - >>> readline.parse_and_bind(r'"\t": complete') + >>> readline.parse_and_bind("tab: complete") >>> readline. readline.__doc__ readline.get_line_buffer( readline.read_init_file( readline.__file__ readline.insert_text( readline.set_completer( diff --git a/Lib/cmd.py b/Lib/cmd.py index cd138a66d099cd..88ee7d3ddc4694 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -73,7 +73,7 @@ class Cmd: nohelp = "*** No help on %s" use_rawinput = 1 - def __init__(self, completekey=r'"\t"', stdin=None, stdout=None): + def __init__(self, completekey='tab', stdin=None, stdout=None): """Instantiate a line-oriented interpreter framework. The optional argument 'completekey' is the readline name of a @@ -108,11 +108,7 @@ def cmdloop(self, intro=None): import readline self.old_completer = readline.get_completer() readline.set_completer(self.complete) - readline_doc = getattr(readline, '__doc__', '') - if readline_doc is not None and 'libedit' in readline_doc: - readline.parse_and_bind("bind "+self.completekey+" rl_complete") - else: - readline.parse_and_bind(self.completekey+": complete") + readline.parse_and_bind(self.completekey+": complete") except ImportError: pass try: diff --git a/Lib/pdb.py b/Lib/pdb.py index e92e6604803c9b..3db3e6a5be1a7b 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -209,7 +209,7 @@ class Pdb(bdb.Bdb, cmd.Cmd): _previous_sigint_handler = None - def __init__(self, completekey=r'"\t"', stdin=None, stdout=None, skip=None, + def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, nosigint=False, readrc=True): bdb.Bdb.__init__(self, skip=skip) cmd.Cmd.__init__(self, completekey, stdin, stdout) diff --git a/Lib/site.py b/Lib/site.py index 342f9e71b8a519..672fa7b000ad02 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -446,9 +446,9 @@ def register_readline(): # completion key, so we set one first and then read the file. readline_doc = getattr(readline, '__doc__', '') if readline_doc is not None and 'libedit' in readline_doc: - readline.parse_and_bind(r'bind "\t" rl_complete') + readline.parse_and_bind('bind ^I rl_complete') else: - readline.parse_and_bind(r'"\t": complete') + readline.parse_and_bind('tab: complete') try: readline.read_init_file() diff --git a/Misc/ACKS b/Misc/ACKS index 0aea3e42754086..8b8c5ad8434bd7 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -784,7 +784,6 @@ Thomas Holmes Craig Holmquist Philip Homburg Naofumi Honda -Constantin Hong Weipeng Hong Jeffrey Honig Rob Hooft From 339b274be6a559304781237eec63ef1579c149f8 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Tue, 7 Nov 2023 04:21:11 +0900 Subject: [PATCH 04/22] gh-102130: Support a tab completion in cmd for Libedit --- Lib/cmd.py | 9 ++++++++- Misc/ACKS | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Lib/cmd.py b/Lib/cmd.py index 88ee7d3ddc4694..98ed99dc80e747 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -108,7 +108,14 @@ def cmdloop(self, intro=None): import readline self.old_completer = readline.get_completer() readline.set_completer(self.complete) - readline.parse_and_bind(self.completekey+": complete") + readline_doc = getattr(readline, '__doc__', '') + if readline_doc is not None and 'libedit' in readline_doc: + if self.completekey == 'tab': + self.completekey = r'"\t"' + readline.parse_and_bind("bind "+self.completekey+" rl_complete") + else: + readline.parse_and_bind(self.completekey+": complete") + except ImportError: pass try: diff --git a/Misc/ACKS b/Misc/ACKS index 8b8c5ad8434bd7..0aea3e42754086 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -784,6 +784,7 @@ Thomas Holmes Craig Holmquist Philip Homburg Naofumi Honda +Constantin Hong Weipeng Hong Jeffrey Honig Rob Hooft From 8cc60fadfb11c7b12a4421f755beb8412bce4e68 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Tue, 7 Nov 2023 06:20:01 +0900 Subject: [PATCH 05/22] Update Lib/cmd.py Co-authored-by: Tian Gao --- Lib/cmd.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/cmd.py b/Lib/cmd.py index 98ed99dc80e747..f93e75a12e11b0 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -108,8 +108,7 @@ def cmdloop(self, intro=None): import readline self.old_completer = readline.get_completer() readline.set_completer(self.complete) - readline_doc = getattr(readline, '__doc__', '') - if readline_doc is not None and 'libedit' in readline_doc: + if 'libedit' in getattr(readline, '__doc__', ''): if self.completekey == 'tab': self.completekey = r'"\t"' readline.parse_and_bind("bind "+self.completekey+" rl_complete") From 21ee6ebbf65d35d6219bd94cd9234f36f07a8b5a Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Tue, 7 Nov 2023 06:25:53 +0900 Subject: [PATCH 06/22] Update 2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst --- .../next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst index a4215c10596930..0f71cc4e8adbab 100644 --- a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst +++ b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst @@ -1 +1 @@ -Support a tab completion for Libedit in :mod:`cmd `, :mod:`site`, and :mod:`pdb`. +Support a tab completion in :mod:`cmd` for Libedit. From 1e8eae7b05190e4b4ae75290a93836d03de75ac2 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Wed, 15 Nov 2023 09:02:49 +0900 Subject: [PATCH 07/22] test_cmd/test: Add basic cmd module tab completion test. --- Lib/test/test_cmd.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py index 951336fa08542d..1db4de396641c1 100644 --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -9,7 +9,10 @@ import doctest import unittest import io +import textwrap from test import support +from test.support.import_helper import import_module +from test.support.pty_helper import run_pty class samplecmdclass(cmd.Cmd): """ @@ -259,6 +262,37 @@ class CmdPrintExceptionClass(cmd.Cmd): def default(self, line): print(sys.exc_info()[:2]) + +@support.requires_subprocess() +class CmdTestReadline(unittest.TestCase): + def setUpClass(): + # Ensure that the readline module is loaded + # If this fails, the test is skipped because SkipTest will be raised + readline = import_module('readline') + + def test_basic_completion(self): + script = textwrap.dedent(""" + import cmd + class simplecmd(cmd.Cmd): + # Add an additional command to prevent complete 'help' command. + def do_EOF(self, args): + return True + + # Concatenate strings so that the output doesn't appear in the source + print('hello' + '!') + simplecmd().cmdloop() + """) + + # List everything starting with '\t\t', there should be multiple matches + # then add 'E' and complete 'OF' to 'EOF' + input = b"\t\tE\t\n" + + output = run_pty(script, input) + + self.assertIn(b'EOF', output) + self.assertIn(b'help', output) + self.assertIn(b'hello!', output) + def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite()) return tests From 03a746274b60c010f717c3a4384f29b004ba81aa Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Wed, 15 Nov 2023 09:04:15 +0900 Subject: [PATCH 08/22] test/test_pdb/test: Enable pdb test for both readline and libedit. --- Lib/test/test_pdb.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 67a4053a2ac8bc..c16ec0f04732be 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3268,8 +3268,6 @@ def setUpClass(): # Ensure that the readline module is loaded # If this fails, the test is skipped because SkipTest will be raised readline = import_module('readline') - if readline.__doc__ and "libedit" in readline.__doc__: - raise unittest.SkipTest("libedit readline is not supported for pdb") def test_basic_completion(self): script = textwrap.dedent(""" From 4667ea46922fd72afc46e012210c4602d3362a33 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Wed, 15 Nov 2023 21:36:33 +0900 Subject: [PATCH 09/22] Revert "test/test_pdb/test: Enable pdb test for both readline and libedit." This reverts commit 03a746274b60c010f717c3a4384f29b004ba81aa. --- Lib/test/test_pdb.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index c16ec0f04732be..67a4053a2ac8bc 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3268,6 +3268,8 @@ def setUpClass(): # Ensure that the readline module is loaded # If this fails, the test is skipped because SkipTest will be raised readline = import_module('readline') + if readline.__doc__ and "libedit" in readline.__doc__: + raise unittest.SkipTest("libedit readline is not supported for pdb") def test_basic_completion(self): script = textwrap.dedent(""" From e71fcf01cd82576dbc7bf60ed5731199071c3523 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Thu, 16 Nov 2023 04:15:49 +0900 Subject: [PATCH 10/22] test_cmd/test: Refine a cmd tab completion test to make it intuitive. --- Lib/test/test_cmd.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py index 1db4de396641c1..432a2b3fbf8c3d 100644 --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -274,24 +274,20 @@ def test_basic_completion(self): script = textwrap.dedent(""" import cmd class simplecmd(cmd.Cmd): - # Add an additional command to prevent complete 'help' command. - def do_EOF(self, args): + def do_tab_completion_test(self, args): + print('tab completion success') return True - # Concatenate strings so that the output doesn't appear in the source - print('hello' + '!') simplecmd().cmdloop() """) - # List everything starting with '\t\t', there should be multiple matches - # then add 'E' and complete 'OF' to 'EOF' - input = b"\t\tE\t\n" + # 't' and complete 'ab_completion_test' to 'tab_completion_test' + input = b"t\t\n" output = run_pty(script, input) - self.assertIn(b'EOF', output) - self.assertIn(b'help', output) - self.assertIn(b'hello!', output) + self.assertIn(b'tab_completion_test', output) + self.assertIn(b'tab completion success', output) def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite()) From e280b1409f3ac953f48443bed027857d85414e82 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Thu, 16 Nov 2023 04:43:18 +0900 Subject: [PATCH 11/22] Update Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst Co-authored-by: Tian Gao --- .../next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst index 0f71cc4e8adbab..1d6a91a3f90e76 100644 --- a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst +++ b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst @@ -1 +1 @@ -Support a tab completion in :mod:`cmd` for Libedit. +Support tab completion in :mod:`cmd` for Libedit. From bc20b5f75495cd41546484c1311e435154f97a84 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Tue, 28 Nov 2023 04:46:04 +0900 Subject: [PATCH 12/22] cmd/docs: Add the fact about the special treatment for the string 'tab'. --- Doc/library/cmd.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index fd5df96dfd0b3d..9de6332672ead8 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -26,6 +26,9 @@ interface. key; it defaults to :kbd:`Tab`. If *completekey* is not :const:`None` and :mod:`readline` is available, command completion is done automatically. + The string ``'tab'`` is treated specially: If the :mod:`readline` module + detects ``libedit``, it will be handled as ``'^I'`` for ``libedit``. + The optional arguments *stdin* and *stdout* specify the input and output file objects that the Cmd instance or subclass instance will use for input and output. If not specified, they will default to :data:`sys.stdin` and From ccbc5c55b48d6731ab3dc496cb0b0ec51733ea74 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Tue, 28 Nov 2023 04:53:13 +0900 Subject: [PATCH 13/22] Update Lib/cmd.py Co-authored-by: Tian Gao --- Lib/cmd.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/cmd.py b/Lib/cmd.py index 7cdaf68883e131..397e51a7219ec6 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -110,8 +110,10 @@ def cmdloop(self, intro=None): readline.set_completer(self.complete) if 'libedit' in getattr(readline, '__doc__', ''): if self.completekey == 'tab': - self.completekey = r'"\t"' - readline.parse_and_bind("bind "+self.completekey+" rl_complete") + # libedit expects "^I" for tab completion instead of "tab" + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("bind "+self.completekey+" rl_complete") else: readline.parse_and_bind(self.completekey+": complete") From 19ef3912f3c1e7ad3b32855509a1d147354915ed Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Fri, 1 Dec 2023 06:32:29 +0900 Subject: [PATCH 14/22] Update Doc/library/cmd.rst Co-authored-by: Petr Viktorin --- Doc/library/cmd.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index 9de6332672ead8..b40b334051771a 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -26,9 +26,12 @@ interface. key; it defaults to :kbd:`Tab`. If *completekey* is not :const:`None` and :mod:`readline` is available, command completion is done automatically. - The string ``'tab'`` is treated specially: If the :mod:`readline` module - detects ``libedit``, it will be handled as ``'^I'`` for ``libedit``. - + The default, ``'tab'``, is treated specially, so that it refers to the + :kbd:`Tab` key on every :data:`readline.backend`. + Specifically, if :data:`readline.backend` is ``libedit``, + ``Cmd`` will use ``'^I'`` instead of ``'tab'``. + Note that other values are not treated this way, and might only work + with a specific backend. The optional arguments *stdin* and *stdout* specify the input and output file objects that the Cmd instance or subclass instance will use for input and output. If not specified, they will default to :data:`sys.stdin` and From a3b6135eb4b3e60ded2ea70d6b72d2844a726ef5 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Fri, 1 Dec 2023 06:39:58 +0900 Subject: [PATCH 15/22] Update Lib/cmd.py Co-authored-by: Petr Viktorin --- Lib/cmd.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/cmd.py b/Lib/cmd.py index 397e51a7219ec6..6fc62cb9c80769 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -110,13 +110,13 @@ def cmdloop(self, intro=None): readline.set_completer(self.complete) if 'libedit' in getattr(readline, '__doc__', ''): if self.completekey == 'tab': - # libedit expects "^I" for tab completion instead of "tab" - readline.parse_and_bind("bind ^I rl_complete") + # libedit uses "^I" instead of "tab" + command_string = "bind ^I rl_complete" else: - readline.parse_and_bind("bind "+self.completekey+" rl_complete") + command_string = f"bind {self.completekey} rl_complete" else: - readline.parse_and_bind(self.completekey+": complete") - + command_string = f"{self.completekey}: complete" + readline.parse_and_bind(command_string) except ImportError: pass try: From 22f1f203158b421f6c63ad9f792f99ceeb269146 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Sat, 2 Dec 2023 00:12:02 +0900 Subject: [PATCH 16/22] cmd/fix: Apply new API --- Lib/cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/cmd.py b/Lib/cmd.py index 6fc62cb9c80769..2e358d6cd5a02d 100644 --- a/Lib/cmd.py +++ b/Lib/cmd.py @@ -108,7 +108,7 @@ def cmdloop(self, intro=None): import readline self.old_completer = readline.get_completer() readline.set_completer(self.complete) - if 'libedit' in getattr(readline, '__doc__', ''): + if readline.backend == "editline": if self.completekey == 'tab': # libedit uses "^I" instead of "tab" command_string = "bind ^I rl_complete" From ede178a2cc7af908ea00b305456cc89a7720d7db Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Sat, 2 Dec 2023 00:19:16 +0900 Subject: [PATCH 17/22] cmd/docs: Fix library name. --- Doc/library/cmd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index b40b334051771a..ed101a21af22e9 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -28,7 +28,7 @@ interface. The default, ``'tab'``, is treated specially, so that it refers to the :kbd:`Tab` key on every :data:`readline.backend`. - Specifically, if :data:`readline.backend` is ``libedit``, + Specifically, if :data:`readline.backend` is ``editline``, ``Cmd`` will use ``'^I'`` instead of ``'tab'``. Note that other values are not treated this way, and might only work with a specific backend. From c00c188731d11c885bbc4f69ce331901fd47b68c Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Sat, 2 Dec 2023 00:12:57 +0900 Subject: [PATCH 18/22] cmd.rst/docs: Add a blank line. --- Doc/library/cmd.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index ed101a21af22e9..dd4379dd7bb83b 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -32,6 +32,7 @@ interface. ``Cmd`` will use ``'^I'`` instead of ``'tab'``. Note that other values are not treated this way, and might only work with a specific backend. + The optional arguments *stdin* and *stdout* specify the input and output file objects that the Cmd instance or subclass instance will use for input and output. If not specified, they will default to :data:`sys.stdin` and From 2dff7ca1e34a999c04a81302566cbb65734332e9 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Sat, 2 Dec 2023 00:47:04 +0900 Subject: [PATCH 19/22] cmd/docs: Add a versionchanged note --- Doc/library/cmd.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index dd4379dd7bb83b..c5d86ba1a92a8c 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -42,6 +42,9 @@ interface. :attr:`use_rawinput` attribute to ``False``, otherwise *stdin* will be ignored. + .. versionchanged:: 3.11 + ``completekey='tab'`` is replaced by ``'^I'`` for ``editline``. + .. _cmd-objects: From 37e6e6df8739e49d3f88db3e94183a7597a56304 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Sat, 2 Dec 2023 01:49:22 +0900 Subject: [PATCH 20/22] 2023-08-07-21-11-24.gh-issue-102130._UyI5i/docs: Fix library name in NEWS file. --- .../next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst index 1d6a91a3f90e76..f582ad5df39e84 100644 --- a/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst +++ b/Misc/NEWS.d/next/Library/2023-08-07-21-11-24.gh-issue-102130._UyI5i.rst @@ -1 +1 @@ -Support tab completion in :mod:`cmd` for Libedit. +Support tab completion in :mod:`cmd` for ``editline``. From 652100f23476b4eda1be33fba8ec235849db69e9 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Mon, 4 Dec 2023 22:31:12 +0900 Subject: [PATCH 21/22] Update Lib/test/test_cmd.py Co-authored-by: Petr Viktorin --- Lib/test/test_cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_cmd.py b/Lib/test/test_cmd.py index 432a2b3fbf8c3d..46ec82b704963d 100644 --- a/Lib/test/test_cmd.py +++ b/Lib/test/test_cmd.py @@ -286,7 +286,7 @@ def do_tab_completion_test(self, args): output = run_pty(script, input) - self.assertIn(b'tab_completion_test', output) + self.assertIn(b'ab_completion_test', output) self.assertIn(b'tab completion success', output) def load_tests(loader, tests, pattern): From ebb81675c82ce469a23d298ff6a707d9d61a37d6 Mon Sep 17 00:00:00 2001 From: Constantin Hong Date: Mon, 4 Dec 2023 22:55:37 +0900 Subject: [PATCH 22/22] Update cmd.rst Co-authored-by: Petr Viktorin --- Doc/library/cmd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst index c5d86ba1a92a8c..1318ffe5a48d53 100644 --- a/Doc/library/cmd.rst +++ b/Doc/library/cmd.rst @@ -42,7 +42,7 @@ interface. :attr:`use_rawinput` attribute to ``False``, otherwise *stdin* will be ignored. - .. versionchanged:: 3.11 + .. versionchanged:: 3.13 ``completekey='tab'`` is replaced by ``'^I'`` for ``editline``.