diff options
-rwxr-xr-x | cmd2.py | 8 | ||||
-rw-r--r-- | docs/argument_processing.rst | 13 | ||||
-rw-r--r-- | tests/test_completion.py | 120 |
3 files changed, 139 insertions, 2 deletions
@@ -803,6 +803,14 @@ class Cmd(cmd.Cmd): If a command has not been entered, then complete against command list. Otherwise try to call complete_<command> to get list of completions. + + This method gets called directly by readline because it is set as the tab-completion function. + + This completer function is called as complete(text, state), for state in 0, 1, 2, …, until it returns a + non-string value. It should return the next possible completion starting with text. + + :param text: str - the current word that user is typing + :param state: int - non-negative integer """ if state == 0: import readline diff --git a/docs/argument_processing.rst b/docs/argument_processing.rst index cc08e4e5..98d622bc 100644 --- a/docs/argument_processing.rst +++ b/docs/argument_processing.rst @@ -166,14 +166,14 @@ The default behavior of ``cmd2`` is to pass the user input directly to your Using the argument parser decorator and also receiving a a list of unknown positional arguments =============================================================================================== If you want all unknown arguments to be passed to your command as a list of strings, then -decorate the command method with the ``@with_argparser_and_list`` decorator. +decorate the command method with the ``@with_argparser_and_unknown_args`` decorator. Here's what it looks like:: dir_parser = argparse.ArgumentParser() dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line") - @with_argparser_and_list(dir_parser) + @with_argparser_and_unknown_args(dir_parser) def do_dir(self, args, unknown): """List contents of current directory.""" # No arguments for this command @@ -188,6 +188,15 @@ Here's what it looks like:: ... +Sub-commands +============ +Sub-commands are supported for commands using either the ``@with_argument_parser`` or +``@with_argparser_and_unknown_args`` decorator. The syntax for supporting them is based on argparse sub-parsers. + +See the subcommands_ example to learn more about how to use sub-commands in your ``cmd2`` application. + +.. _subcommands: https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py + Deprecated optparse support =========================== diff --git a/tests/test_completion.py b/tests/test_completion.py index a0ee503c..f0efae62 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -10,9 +10,11 @@ Released under MIT license, see LICENSE file """ import argparse import os +import readline import sys import cmd2 +import mock import pytest @@ -36,6 +38,100 @@ def test_cmd2_command_completion_single_end(cmd2_app): # It is at end of line, so extra space is present assert cmd2_app.completenames(text, line, begidx, endidx) == ['help '] +def test_complete_command_single_end(cmd2_app): + text = 'he' + line = 'he' + state = 0 + endidx = len(line) + begidx = endidx - len(text) + + def get_line(): + return line + + def get_begidx(): + return begidx + + def get_endidx(): + return endidx + + with mock.patch.object(readline, 'get_line_buffer', get_line): + with mock.patch.object(readline, 'get_begidx', get_begidx): + with mock.patch.object(readline, 'get_endidx', get_endidx): + # Run the readline tab-completion function with readline mocks in place + completion = cmd2_app.complete(text, state) + assert completion == 'help ' + +def test_complete_command_invalid_state(cmd2_app): + text = 'he' + line = 'he' + state = 1 + endidx = len(line) + begidx = endidx - len(text) + + def get_line(): + return line + + def get_begidx(): + return begidx + + def get_endidx(): + return endidx + + with mock.patch.object(readline, 'get_line_buffer', get_line): + with mock.patch.object(readline, 'get_begidx', get_begidx): + with mock.patch.object(readline, 'get_endidx', get_endidx): + with pytest.raises(AttributeError): + # Run the readline tab-completion function with readline mocks in place and cause an exception + completion = cmd2_app.complete(text, state) + +def test_complete_empty_arg(cmd2_app): + text = '' + line = 'help ' + state = 0 + endidx = len(line) + begidx = endidx - len(text) + + def get_line(): + return line + + def get_begidx(): + return begidx + + def get_endidx(): + return endidx + + with mock.patch.object(readline, 'get_line_buffer', get_line): + with mock.patch.object(readline, 'get_begidx', get_begidx): + with mock.patch.object(readline, 'get_endidx', get_endidx): + # Run the readline tab-completion function with readline mocks in place + completion = cmd2_app.complete(text, state) + + assert completion == cmd2_app.complete_help(text, line, begidx, endidx)[0] + +def test_complete_bogus_command(cmd2_app): + text = '' + line = 'fizbuzz ' + state = 0 + endidx = len(line) + begidx = endidx - len(text) + + def get_line(): + return line + + def get_begidx(): + return begidx + + def get_endidx(): + return endidx + + with mock.patch.object(readline, 'get_line_buffer', get_line): + with mock.patch.object(readline, 'get_begidx', get_begidx): + with mock.patch.object(readline, 'get_endidx', get_endidx): + # Run the readline tab-completion function with readline mocks in place + completion = cmd2_app.complete(text, state) + + assert completion is None + def test_cmd2_command_completion_is_case_insensitive_by_default(cmd2_app): text = 'HE' line = 'HE' @@ -420,3 +516,27 @@ def test_cmd2_subcommand_completion_after_subcommand(sc_app): # It is at end of line, so extra space is present assert sc_app.complete_subcommand(text, line, begidx, endidx) == [] + + +def test_complete_subcommand_single_end(sc_app): + text = 'f' + line = 'base f' + endidx = len(line) + begidx = endidx - len(text) + state = 0 + + def get_line(): + return line + + def get_begidx(): + return begidx + + def get_endidx(): + return endidx + + with mock.patch.object(readline, 'get_line_buffer', get_line): + with mock.patch.object(readline, 'get_begidx', get_begidx): + with mock.patch.object(readline, 'get_endidx', get_endidx): + # Run the readline tab-completion function with readline mocks in place + completion = sc_app.complete(text, state) + assert completion == 'foo ' |