Skip to content

Commit ae7b770

Browse files
Constantin1489gaogaotiantian
authored andcommitted
pythongh-102130: Support tab completion in cmd for Libedit. (pythonGH-107748)
--- Co-authored-by: Tian Gao <[email protected]>
1 parent 8c65254 commit ae7b770

File tree

5 files changed

+51
-1
lines changed

5 files changed

+51
-1
lines changed

Doc/library/cmd.rst

+10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ interface.
2626
key; it defaults to :kbd:`Tab`. If *completekey* is not :const:`None` and
2727
:mod:`readline` is available, command completion is done automatically.
2828

29+
The default, ``'tab'``, is treated specially, so that it refers to the
30+
:kbd:`Tab` key on every :data:`readline.backend`.
31+
Specifically, if :data:`readline.backend` is ``editline``,
32+
``Cmd`` will use ``'^I'`` instead of ``'tab'``.
33+
Note that other values are not treated this way, and might only work
34+
with a specific backend.
35+
2936
The optional arguments *stdin* and *stdout* specify the input and output file
3037
objects that the Cmd instance or subclass instance will use for input and
3138
output. If not specified, they will default to :data:`sys.stdin` and
@@ -35,6 +42,9 @@ interface.
3542
:attr:`use_rawinput` attribute to ``False``, otherwise *stdin* will be
3643
ignored.
3744

45+
.. versionchanged:: 3.13
46+
``completekey='tab'`` is replaced by ``'^I'`` for ``editline``.
47+
3848

3949
.. _cmd-objects:
4050

Lib/cmd.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,15 @@ def cmdloop(self, intro=None):
108108
import readline
109109
self.old_completer = readline.get_completer()
110110
readline.set_completer(self.complete)
111-
readline.parse_and_bind(self.completekey+": complete")
111+
if readline.backend == "editline":
112+
if self.completekey == 'tab':
113+
# libedit uses "^I" instead of "tab"
114+
command_string = "bind ^I rl_complete"
115+
else:
116+
command_string = f"bind {self.completekey} rl_complete"
117+
else:
118+
command_string = f"{self.completekey}: complete"
119+
readline.parse_and_bind(command_string)
112120
except ImportError:
113121
pass
114122
try:

Lib/test/test_cmd.py

+30
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
import doctest
1010
import unittest
1111
import io
12+
import textwrap
1213
from test import support
14+
from test.support.import_helper import import_module
15+
from test.support.pty_helper import run_pty
1316

1417
class samplecmdclass(cmd.Cmd):
1518
"""
@@ -259,6 +262,33 @@ class CmdPrintExceptionClass(cmd.Cmd):
259262
def default(self, line):
260263
print(sys.exc_info()[:2])
261264

265+
266+
@support.requires_subprocess()
267+
class CmdTestReadline(unittest.TestCase):
268+
def setUpClass():
269+
# Ensure that the readline module is loaded
270+
# If this fails, the test is skipped because SkipTest will be raised
271+
readline = import_module('readline')
272+
273+
def test_basic_completion(self):
274+
script = textwrap.dedent("""
275+
import cmd
276+
class simplecmd(cmd.Cmd):
277+
def do_tab_completion_test(self, args):
278+
print('tab completion success')
279+
return True
280+
281+
simplecmd().cmdloop()
282+
""")
283+
284+
# 't' and complete 'ab_completion_test' to 'tab_completion_test'
285+
input = b"t\t\n"
286+
287+
output = run_pty(script, input)
288+
289+
self.assertIn(b'ab_completion_test', output)
290+
self.assertIn(b'tab completion success', output)
291+
262292
def load_tests(loader, tests, pattern):
263293
tests.addTest(doctest.DocTestSuite())
264294
return tests

Misc/ACKS

+1
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ Thomas Holmes
788788
Craig Holmquist
789789
Philip Homburg
790790
Naofumi Honda
791+
Constantin Hong
791792
Weipeng Hong
792793
Jeffrey Honig
793794
Rob Hooft
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support tab completion in :mod:`cmd` for ``editline``.

0 commit comments

Comments
 (0)