summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2018-01-20 18:04:43 -0500
committerTodd Leonhardt <todd.leonhardt@gmail.com>2018-01-20 18:04:43 -0500
commitbd948d727e0e13fa5fd77199c06fcd3dfdda9b39 (patch)
treebbb7ae35cb76becccddfe7dd4258c87b2c463b68
parent5550ab73a91d2834e6bda72eb3889998ad59be51 (diff)
downloadcmd2-git-bd948d727e0e13fa5fd77199c06fcd3dfdda9b39.tar.gz
Added unit tests for newly-overridden complete() method
Also added a section on Sub-commands to the documentation.
-rwxr-xr-xcmd2.py8
-rw-r--r--docs/argument_processing.rst13
-rw-r--r--tests/test_completion.py120
3 files changed, 139 insertions, 2 deletions
diff --git a/cmd2.py b/cmd2.py
index a533bb7a..39522f27 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -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 '