Skip to content

Commit

Permalink
pythongh-102130: Support tab completion in cmd for Libedit. (pythonGH…
Browse files Browse the repository at this point in the history
…-107748)

---

Co-authored-by: Tian Gao <[email protected]>
  • Loading branch information
2 people authored and Glyphack committed Jan 27, 2024
1 parent c8793e3 commit 530517a
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 1 deletion.
10 changes: 10 additions & 0 deletions Doc/library/cmd.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ interface.
key; it defaults to :kbd:`Tab`. If *completekey* is not :const:`None` and
:mod:`readline` is available, command completion is done automatically.

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 ``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.

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
Expand All @@ -35,6 +42,9 @@ interface.
:attr:`use_rawinput` attribute to ``False``, otherwise *stdin* will be
ignored.

.. versionchanged:: 3.13
``completekey='tab'`` is replaced by ``'^I'`` for ``editline``.


.. _cmd-objects:

Expand Down
10 changes: 9 additions & 1 deletion Lib/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,15 @@ 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")
if readline.backend == "editline":
if self.completekey == 'tab':
# libedit uses "^I" instead of "tab"
command_string = "bind ^I rl_complete"
else:
command_string = f"bind {self.completekey} rl_complete"
else:
command_string = f"{self.completekey}: complete"
readline.parse_and_bind(command_string)
except ImportError:
pass
try:
Expand Down
30 changes: 30 additions & 0 deletions Lib/test/test_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -259,6 +262,33 @@ 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):
def do_tab_completion_test(self, args):
print('tab completion success')
return True
simplecmd().cmdloop()
""")

# 't' and complete 'ab_completion_test' to 'tab_completion_test'
input = b"t\t\n"

output = run_pty(script, input)

self.assertIn(b'ab_completion_test', output)
self.assertIn(b'tab completion success', output)

def load_tests(loader, tests, pattern):
tests.addTest(doctest.DocTestSuite())
return tests
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,7 @@ Thomas Holmes
Craig Holmquist
Philip Homburg
Naofumi Honda
Constantin Hong
Weipeng Hong
Jeffrey Honig
Rob Hooft
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support tab completion in :mod:`cmd` for ``editline``.

0 comments on commit 530517a

Please sign in to comment.