Skip to content

Commit 13aac11

Browse files
authored
Merge pull request #353 from python-cmd2/unhackify
Unhackify
2 parents 9d9e843 + ef99976 commit 13aac11

File tree

8 files changed

+272
-198
lines changed

8 files changed

+272
-198
lines changed

.gitignore

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
1+
# Python development, test, and build
12
__pycache__
23
build
34
dist
45
cmd2.egg-info
5-
.idea
66
.cache
77
*.pyc
8-
.coverage
98
.tox
10-
htmlcov
119
.pytest_cache
10+
11+
# Code Coverage
12+
.coverage
13+
htmlcov
14+
15+
# PyCharm
16+
.idea
17+
18+
# Visual Studio Code
19+
.vscode

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
## 0.8.5 (April 15, 2018)
2+
* Bug Fixes
3+
* Fixed a bug with all argument decorators where the wrapped function wasn't returning a value and thus couldn't cause the cmd2 app to quit
4+
5+
* Enhancements
6+
* Added support for verbose help with -v where it lists a brief summary of what each command does
7+
* Added support for categorizing commands into groups within the help menu
8+
* See the [Grouping Commands](http://cmd2.readthedocs.io/en/latest/argument_processing.html?highlight=verbose#grouping-commands) section of the docs for more info
9+
* See [help_categories.py](https://github.com/python-cmd2/cmd2/blob/master/examples/help_categories.py) for an example
10+
* Tab completion of paths now supports ~user user path expansion
11+
* Simplified implementation of various tab completion functions so they no longer require ``ctypes``
12+
* Expanded documentation of ``display_matches`` list to clarify its purpose. See cmd2.py for this documentation.
13+
* Adding opening quote to tab completion if any of the matches have a space.
14+
15+
* **Python 2 EOL notice**
16+
* This is the last release where new features will be added to ``cmd2`` for Python 2.7
17+
* The 0.9.0 release of ``cmd2`` will support Python 3.4+ only
18+
* Additional 0.8.x releases may be created to supply bug fixes for Python 2.7 up until August 31, 2018
19+
* After August 31, 2018 not even bug fixes will be provided for Python 2.7
20+
121
## 0.8.4 (April 10, 2018)
222
* Bug Fixes
323
* Fixed conditional dependency issue in setup.py that was in 0.8.3.

cmd2.py

Lines changed: 91 additions & 167 deletions
Large diffs are not rendered by default.

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
# The short X.Y version.
6363
version = '0.8'
6464
# The full version, including alpha/beta/rc tags.
65-
release = '0.8.4'
65+
release = '0.8.5'
6666

6767
# The language for content autogenerated by Sphinx. Refer to documentation
6868
# for a list of supported languages.

examples/tab_completion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
from cmd2 import with_argparser, with_argument_list
99

1010
# List of strings used with flag and index based completion functions
11-
food_item_strs = ['Pizza', 'Hamburger', 'Ham', 'Potato']
12-
sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football']
11+
food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato']
12+
sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball']
1313

1414

1515
class TabCompleteExample(cmd2.Cmd):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import setuptools
99
from setuptools import setup
1010

11-
VERSION = '0.8.4'
11+
VERSION = '0.8.5'
1212
DESCRIPTION = "cmd2 - a tool for building interactive command line applications in Python"
1313
LONG_DESCRIPTION = """cmd2 is a tool for building interactive command line applications in Python. Its goal is to make
1414
it quick and easy for developers to build feature-rich and user-friendly interactive command line applications. It

tests/test_cmd2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727

2828
def test_ver():
29-
assert cmd2.__version__ == '0.8.4'
29+
assert cmd2.__version__ == '0.8.5'
3030

3131

3232
def test_empty_statement(base_app):

tests/test_completion.py

Lines changed: 145 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,17 @@
2929
pass
3030

3131

32-
@pytest.fixture
33-
def cmd2_app():
34-
c = cmd2.Cmd()
35-
return c
36-
37-
3832
# List of strings used with completion functions
39-
food_item_strs = ['Pizza', 'Hamburger', 'Ham', 'Potato']
33+
food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato']
4034
sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball']
41-
delimited_strs = ['/home/user/file.txt', '/home/user/prog.c', '/home/otheruser/maps']
35+
delimited_strs = \
36+
[
37+
'/home/user/file.txt',
38+
'/home/user/file space.txt',
39+
'/home/user/prog.c',
40+
'/home/other user/maps',
41+
'/home/other user/tests'
42+
]
4243

4344
# Dictionary used with flag based completion functions
4445
flag_dict = \
@@ -59,6 +60,33 @@ def cmd2_app():
5960
2: sport_item_strs, # Tab-complete sport items at index 2 in command line
6061
}
6162

63+
64+
class CompletionsExample(cmd2.Cmd):
65+
"""
66+
Example cmd2 application used to exercise tab-completion tests
67+
"""
68+
def __init__(self):
69+
cmd2.Cmd.__init__(self)
70+
71+
def do_test_basic(self, args):
72+
pass
73+
74+
def complete_test_basic(self, text, line, begidx, endidx):
75+
return self.basic_complete(text, line, begidx, endidx, food_item_strs)
76+
77+
def do_test_delimited(self, args):
78+
pass
79+
80+
def complete_test_delimited(self, text, line, begidx, endidx):
81+
return self.delimiter_complete(text, line, begidx, endidx, delimited_strs, '/')
82+
83+
84+
@pytest.fixture
85+
def cmd2_app():
86+
c = CompletionsExample()
87+
return c
88+
89+
6290
def complete_tester(text, line, begidx, endidx, app):
6391
"""
6492
This is a convenience function to test cmd2.complete() since
@@ -425,12 +453,12 @@ def test_delimiter_completion(cmd2_app):
425453

426454
cmd2_app.delimiter_complete(text, line, begidx, endidx, delimited_strs, '/')
427455

428-
# Remove duplicates from display_matches and sort it. This is typically done in the display function.
456+
# Remove duplicates from display_matches and sort it. This is typically done in complete().
429457
display_set = set(cmd2_app.display_matches)
430458
display_list = list(display_set)
431459
display_list.sort()
432460

433-
assert display_list == ['otheruser', 'user']
461+
assert display_list == ['other user', 'user']
434462

435463
def test_flag_based_completion_single(cmd2_app):
436464
text = 'Pi'
@@ -638,6 +666,113 @@ def test_parseline_expands_shortcuts(cmd2_app):
638666
assert args == 'cat foobar.txt'
639667
assert line.replace('!', 'shell ') == out_line
640668

669+
def test_add_opening_quote_basic_no_text(cmd2_app):
670+
text = ''
671+
line = 'test_basic {}'.format(text)
672+
endidx = len(line)
673+
begidx = endidx - len(text)
674+
675+
# The whole list will be returned with no opening quotes added
676+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
677+
assert first_match is not None and cmd2_app.completion_matches == sorted(food_item_strs)
678+
679+
def test_add_opening_quote_basic_nothing_added(cmd2_app):
680+
text = 'P'
681+
line = 'test_basic {}'.format(text)
682+
endidx = len(line)
683+
begidx = endidx - len(text)
684+
685+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
686+
assert first_match is not None and cmd2_app.completion_matches == ['Pizza', 'Potato']
687+
688+
def test_add_opening_quote_basic_quote_added(cmd2_app):
689+
text = 'Ha'
690+
line = 'test_basic {}'.format(text)
691+
endidx = len(line)
692+
begidx = endidx - len(text)
693+
694+
expected = sorted(['"Ham', '"Ham Sandwich'])
695+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
696+
assert first_match is not None and cmd2_app.completion_matches == expected
697+
698+
def test_add_opening_quote_basic_text_is_common_prefix(cmd2_app):
699+
# This tests when the text entered is the same as the common prefix of the matches
700+
text = 'Ham'
701+
line = 'test_basic {}'.format(text)
702+
endidx = len(line)
703+
begidx = endidx - len(text)
704+
705+
expected = sorted(['"Ham', '"Ham Sandwich'])
706+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
707+
assert first_match is not None and cmd2_app.completion_matches == expected
708+
709+
def test_add_opening_quote_delimited_no_text(cmd2_app):
710+
text = ''
711+
line = 'test_delimited {}'.format(text)
712+
endidx = len(line)
713+
begidx = endidx - len(text)
714+
715+
# The whole list will be returned with no opening quotes added
716+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
717+
assert first_match is not None and cmd2_app.completion_matches == sorted(delimited_strs)
718+
719+
def test_add_opening_quote_delimited_nothing_added(cmd2_app):
720+
text = '/ho'
721+
line = 'test_delimited {}'.format(text)
722+
endidx = len(line)
723+
begidx = endidx - len(text)
724+
725+
expected_matches = sorted(delimited_strs)
726+
expected_display = sorted(['other user', 'user'])
727+
728+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
729+
assert first_match is not None and \
730+
cmd2_app.completion_matches == expected_matches and \
731+
cmd2_app.display_matches == expected_display
732+
733+
def test_add_opening_quote_delimited_quote_added(cmd2_app):
734+
text = '/home/user/fi'
735+
line = 'test_delimited {}'.format(text)
736+
endidx = len(line)
737+
begidx = endidx - len(text)
738+
739+
expected_common_prefix = '"/home/user/file'
740+
expected_display = sorted(['file.txt', 'file space.txt'])
741+
742+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
743+
assert first_match is not None and \
744+
os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix and \
745+
cmd2_app.display_matches == expected_display
746+
747+
def test_add_opening_quote_delimited_text_is_common_prefix(cmd2_app):
748+
# This tests when the text entered is the same as the common prefix of the matches
749+
text = '/home/user/file'
750+
line = 'test_delimited {}'.format(text)
751+
endidx = len(line)
752+
begidx = endidx - len(text)
753+
754+
expected_common_prefix = '"/home/user/file'
755+
expected_display = sorted(['file.txt', 'file space.txt'])
756+
757+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
758+
assert first_match is not None and \
759+
os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix and \
760+
cmd2_app.display_matches == expected_display
761+
762+
def test_add_opening_quote_delimited_space_in_prefix(cmd2_app):
763+
# This test when a space appears before the part of the string that is the display match
764+
text = '/home/oth'
765+
line = 'test_delimited {}'.format(text)
766+
endidx = len(line)
767+
begidx = endidx - len(text)
768+
769+
expected_common_prefix = '"/home/other user/'
770+
expected_display = ['maps', 'tests']
771+
772+
first_match = complete_tester(text, line, begidx, endidx, cmd2_app)
773+
assert first_match is not None and \
774+
os.path.commonprefix(cmd2_app.completion_matches) == expected_common_prefix and \
775+
cmd2_app.display_matches == expected_display
641776

642777
class SubcommandsExample(cmd2.Cmd):
643778
"""
@@ -787,19 +922,6 @@ def test_subcommand_tab_completion_with_no_completer(sc_app):
787922
first_match = complete_tester(text, line, begidx, endidx, sc_app)
788923
assert first_match is None
789924

790-
def test_subcommand_tab_completion_add_quote(sc_app):
791-
# This makes sure an opening quote is added to the readline line buffer
792-
text = 'Space'
793-
line = 'base sport {}'.format(text)
794-
endidx = len(line)
795-
begidx = endidx - len(text)
796-
797-
first_match = complete_tester(text, line, begidx, endidx, sc_app)
798-
799-
# No matches are returned when an opening quote is added to the screen
800-
assert first_match is None
801-
assert readline.get_line_buffer() == 'base sport "Space Ball" '
802-
803925
def test_subcommand_tab_completion_space_in_text(sc_app):
804926
text = 'B'
805927
line = 'base sport "Space {}'.format(text)

0 commit comments

Comments
 (0)