Skip to content

Commit bd948d7

Browse files
committed
Added unit tests for newly-overridden complete() method
Also added a section on Sub-commands to the documentation.
1 parent 5550ab7 commit bd948d7

File tree

3 files changed

+139
-2
lines changed

3 files changed

+139
-2
lines changed

cmd2.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,14 @@ def complete(self, text, state):
803803
804804
If a command has not been entered, then complete against command list.
805805
Otherwise try to call complete_<command> to get list of completions.
806+
807+
This method gets called directly by readline because it is set as the tab-completion function.
808+
809+
This completer function is called as complete(text, state), for state in 0, 1, 2, …, until it returns a
810+
non-string value. It should return the next possible completion starting with text.
811+
812+
:param text: str - the current word that user is typing
813+
:param state: int - non-negative integer
806814
"""
807815
if state == 0:
808816
import readline

docs/argument_processing.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,14 @@ The default behavior of ``cmd2`` is to pass the user input directly to your
166166
Using the argument parser decorator and also receiving a a list of unknown positional arguments
167167
===============================================================================================
168168
If you want all unknown arguments to be passed to your command as a list of strings, then
169-
decorate the command method with the ``@with_argparser_and_list`` decorator.
169+
decorate the command method with the ``@with_argparser_and_unknown_args`` decorator.
170170

171171
Here's what it looks like::
172172

173173
dir_parser = argparse.ArgumentParser()
174174
dir_parser.add_argument('-l', '--long', action='store_true', help="display in long format with one item per line")
175175

176-
@with_argparser_and_list(dir_parser)
176+
@with_argparser_and_unknown_args(dir_parser)
177177
def do_dir(self, args, unknown):
178178
"""List contents of current directory."""
179179
# No arguments for this command
@@ -188,6 +188,15 @@ Here's what it looks like::
188188

189189
...
190190

191+
Sub-commands
192+
============
193+
Sub-commands are supported for commands using either the ``@with_argument_parser`` or
194+
``@with_argparser_and_unknown_args`` decorator. The syntax for supporting them is based on argparse sub-parsers.
195+
196+
See the subcommands_ example to learn more about how to use sub-commands in your ``cmd2`` application.
197+
198+
.. _subcommands: https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py
199+
191200
Deprecated optparse support
192201
===========================
193202

tests/test_completion.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
"""
1111
import argparse
1212
import os
13+
import readline
1314
import sys
1415

1516
import cmd2
17+
import mock
1618
import pytest
1719

1820

@@ -36,6 +38,100 @@ def test_cmd2_command_completion_single_end(cmd2_app):
3638
# It is at end of line, so extra space is present
3739
assert cmd2_app.completenames(text, line, begidx, endidx) == ['help ']
3840

41+
def test_complete_command_single_end(cmd2_app):
42+
text = 'he'
43+
line = 'he'
44+
state = 0
45+
endidx = len(line)
46+
begidx = endidx - len(text)
47+
48+
def get_line():
49+
return line
50+
51+
def get_begidx():
52+
return begidx
53+
54+
def get_endidx():
55+
return endidx
56+
57+
with mock.patch.object(readline, 'get_line_buffer', get_line):
58+
with mock.patch.object(readline, 'get_begidx', get_begidx):
59+
with mock.patch.object(readline, 'get_endidx', get_endidx):
60+
# Run the readline tab-completion function with readline mocks in place
61+
completion = cmd2_app.complete(text, state)
62+
assert completion == 'help '
63+
64+
def test_complete_command_invalid_state(cmd2_app):
65+
text = 'he'
66+
line = 'he'
67+
state = 1
68+
endidx = len(line)
69+
begidx = endidx - len(text)
70+
71+
def get_line():
72+
return line
73+
74+
def get_begidx():
75+
return begidx
76+
77+
def get_endidx():
78+
return endidx
79+
80+
with mock.patch.object(readline, 'get_line_buffer', get_line):
81+
with mock.patch.object(readline, 'get_begidx', get_begidx):
82+
with mock.patch.object(readline, 'get_endidx', get_endidx):
83+
with pytest.raises(AttributeError):
84+
# Run the readline tab-completion function with readline mocks in place and cause an exception
85+
completion = cmd2_app.complete(text, state)
86+
87+
def test_complete_empty_arg(cmd2_app):
88+
text = ''
89+
line = 'help '
90+
state = 0
91+
endidx = len(line)
92+
begidx = endidx - len(text)
93+
94+
def get_line():
95+
return line
96+
97+
def get_begidx():
98+
return begidx
99+
100+
def get_endidx():
101+
return endidx
102+
103+
with mock.patch.object(readline, 'get_line_buffer', get_line):
104+
with mock.patch.object(readline, 'get_begidx', get_begidx):
105+
with mock.patch.object(readline, 'get_endidx', get_endidx):
106+
# Run the readline tab-completion function with readline mocks in place
107+
completion = cmd2_app.complete(text, state)
108+
109+
assert completion == cmd2_app.complete_help(text, line, begidx, endidx)[0]
110+
111+
def test_complete_bogus_command(cmd2_app):
112+
text = ''
113+
line = 'fizbuzz '
114+
state = 0
115+
endidx = len(line)
116+
begidx = endidx - len(text)
117+
118+
def get_line():
119+
return line
120+
121+
def get_begidx():
122+
return begidx
123+
124+
def get_endidx():
125+
return endidx
126+
127+
with mock.patch.object(readline, 'get_line_buffer', get_line):
128+
with mock.patch.object(readline, 'get_begidx', get_begidx):
129+
with mock.patch.object(readline, 'get_endidx', get_endidx):
130+
# Run the readline tab-completion function with readline mocks in place
131+
completion = cmd2_app.complete(text, state)
132+
133+
assert completion is None
134+
39135
def test_cmd2_command_completion_is_case_insensitive_by_default(cmd2_app):
40136
text = 'HE'
41137
line = 'HE'
@@ -420,3 +516,27 @@ def test_cmd2_subcommand_completion_after_subcommand(sc_app):
420516

421517
# It is at end of line, so extra space is present
422518
assert sc_app.complete_subcommand(text, line, begidx, endidx) == []
519+
520+
521+
def test_complete_subcommand_single_end(sc_app):
522+
text = 'f'
523+
line = 'base f'
524+
endidx = len(line)
525+
begidx = endidx - len(text)
526+
state = 0
527+
528+
def get_line():
529+
return line
530+
531+
def get_begidx():
532+
return begidx
533+
534+
def get_endidx():
535+
return endidx
536+
537+
with mock.patch.object(readline, 'get_line_buffer', get_line):
538+
with mock.patch.object(readline, 'get_begidx', get_begidx):
539+
with mock.patch.object(readline, 'get_endidx', get_endidx):
540+
# Run the readline tab-completion function with readline mocks in place
541+
completion = sc_app.complete(text, state)
542+
assert completion == 'foo '

0 commit comments

Comments
 (0)