Skip to content

Commit 61d5703

Browse files
authored
Merge pull request #553 from python-cmd2/argparse_conversion
Argparse conversion
2 parents 87fdda1 + 46bd94a commit 61d5703

File tree

8 files changed

+324
-260
lines changed

8 files changed

+324
-260
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* Always - output methods **never** strip ANSI escape sequences, regardless of the output destination
2323
* Never - output methods strip all ANSI escape sequences
2424
* Added ``macro`` command to create macros, which are similar to aliases, but can take arguments when called
25-
* ``alias`` is now an argparse command with subcommands to create, list, and delete aliases
25+
* All cmd2 command functions have been converted to use argparse.
2626
* Deprecations
2727
* Deprecated the built-in ``cmd2`` support for colors including ``Cmd.colorize()`` and ``Cmd._colorcodes``
2828
* Deletions (potentially breaking changes)

cmd2/cmd2.py

Lines changed: 189 additions & 200 deletions
Large diffs are not rendered by default.

docs/freefeatures.rst

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,9 @@ More Python examples:
174174
Type "help", "copyright", "credits" or "license" for more information.
175175
(CmdLineApp)
176176

177-
Invoke python command, shell, or script
178-
179-
py <command>: Executes a Python command.
180-
py: Enters interactive Python mode.
181-
End with ``Ctrl-D`` (Unix) / ``Ctrl-Z`` (Windows), ``quit()``, '`exit()``.
182-
Non-python commands can be issued with ``app("your command")``.
183-
Run python code from external script files with ``run("script.py")``
177+
End with `Ctrl-D` (Unix) / `Ctrl-Z` (Windows), `quit()`, `exit()`.
178+
Non-python commands can be issued with: app("your command")
179+
Run python code from external script files with: run("script.py")
184180

185181
>>> import os
186182
>>> os.uname()

tests/conftest.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,16 @@
3838
================================================================================
3939
alias Manage aliases
4040
edit Edit a file in a text editor
41-
help List available commands with "help" or detailed help with "help cmd"
41+
help List available commands or provide detailed help for a specific command
4242
history View, run, edit, save, or clear previously entered commands
43-
load Runs commands in script file that is encoded as either ASCII or UTF-8 text
43+
load Run commands in script file that is encoded as either ASCII or UTF-8 text
4444
macro Manage macros
45-
py Invoke python command, shell, or script
46-
pyscript Runs a python script file inside the console
47-
quit Exits this application
48-
set Sets a settable parameter or shows current settings of parameters
45+
py Invoke Python command or shell
46+
pyscript Run a Python script file inside the console
47+
quit Exit this application
48+
set Set a settable parameter or show current settings of parameters
4949
shell Execute a command as if at the OS prompt
50-
shortcuts Lists shortcuts available
50+
shortcuts List available shortcuts
5151
"""
5252

5353
# Help text for the history command
@@ -66,12 +66,12 @@
6666
-h, --help show this help message and exit
6767
-r, --run run selected history items
6868
-e, --edit edit and then run selected history items
69-
-s, --script script format; no separation lines
69+
-s, --script output commands in script format
7070
-o, --output-file FILE
7171
output commands to a script file
7272
-t, --transcript TRANSCRIPT
7373
output commands and results to a transcript file
74-
-c, --clear clears all history
74+
-c, --clear clear all history
7575
"""
7676

7777
# Output from the shortcuts command with default built-in shortcuts

tests/test_argparse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def do_base(self, args):
217217
func(self, args)
218218
else:
219219
# No subcommand was provided, so call help
220-
self.do_help(['base'])
220+
self.do_help('base')
221221

222222
@pytest.fixture
223223
def subcommand_app():

tests/test_cmd2.py

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def test_base_argparse_help(base_app, capsys):
7070
assert out1 == out2
7171
assert out1[0].startswith('Usage: set')
7272
assert out1[1] == ''
73-
assert out1[2].startswith('Sets a settable parameter')
73+
assert out1[2].startswith('Set a settable parameter')
7474

7575
def test_base_invalid_option(base_app, capsys):
7676
run_cmd(base_app, 'set -z')
@@ -184,6 +184,30 @@ def test_set_quiet(base_app):
184184
assert out == ['quiet: True']
185185

186186

187+
class OnChangeHookApp(cmd2.Cmd):
188+
def __init__(self, *args, **kwargs):
189+
super().__init__(*args, **kwargs)
190+
191+
def _onchange_quiet(self, old, new) -> None:
192+
"""Runs when quiet is changed via set command"""
193+
self.poutput("You changed quiet")
194+
195+
@pytest.fixture
196+
def onchange_app():
197+
app = OnChangeHookApp()
198+
app.stdout = utils.StdSim(app.stdout)
199+
return app
200+
201+
def test_set_onchange_hook(onchange_app):
202+
out = run_cmd(onchange_app, 'set quiet True')
203+
expected = normalize("""
204+
quiet - was: False
205+
now: True
206+
You changed quiet
207+
""")
208+
assert out == expected
209+
210+
187211
def test_base_shell(base_app, monkeypatch):
188212
m = mock.Mock()
189213
monkeypatch.setattr("{}.Popen".format('subprocess'), m)
@@ -247,7 +271,7 @@ def test_pyscript_with_exception(base_app, capsys, request):
247271
def test_pyscript_requires_an_argument(base_app, capsys):
248272
run_cmd(base_app, "pyscript")
249273
out, err = capsys.readouterr()
250-
assert err.startswith('ERROR: pyscript command requires at least 1 argument ...')
274+
assert "the following arguments are required: script_path" in err
251275

252276

253277
def test_base_error(base_app):
@@ -477,7 +501,7 @@ def test_load_with_empty_args(base_app, capsys):
477501
out, err = capsys.readouterr()
478502

479503
# The load command requires a file path argument, so we should get an error message
480-
assert "load command requires a file path" in str(err)
504+
assert "the following arguments are required" in str(err)
481505
assert base_app.cmdqueue == []
482506

483507

@@ -614,8 +638,7 @@ def test_base_relative_load(base_app, request):
614638
def test_relative_load_requires_an_argument(base_app, capsys):
615639
run_cmd(base_app, '_relative_load')
616640
out, err = capsys.readouterr()
617-
assert out == ''
618-
assert err.startswith('ERROR: _relative_load command requires a file path:\n')
641+
assert 'Error: the following arguments' in err
619642
assert base_app.cmdqueue == []
620643

621644

@@ -858,7 +881,8 @@ def test_edit_file(base_app, request, monkeypatch):
858881
run_cmd(base_app, 'edit {}'.format(filename))
859882

860883
# We think we have an editor, so should expect a system call
861-
m.assert_called_once_with('"{}" "{}"'.format(base_app.editor, filename))
884+
m.assert_called_once_with('{} {}'.format(utils.quote_string_if_needed(base_app.editor),
885+
utils.quote_string_if_needed(filename)))
862886

863887
def test_edit_file_with_spaces(base_app, request, monkeypatch):
864888
# Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock
@@ -874,7 +898,8 @@ def test_edit_file_with_spaces(base_app, request, monkeypatch):
874898
run_cmd(base_app, 'edit "{}"'.format(filename))
875899

876900
# We think we have an editor, so should expect a system call
877-
m.assert_called_once_with('"{}" "{}"'.format(base_app.editor, filename))
901+
m.assert_called_once_with('{} {}'.format(utils.quote_string_if_needed(base_app.editor),
902+
utils.quote_string_if_needed(filename)))
878903

879904
def test_edit_blank(base_app, monkeypatch):
880905
# Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock
@@ -1250,16 +1275,16 @@ def test_help_cat_verbose(helpcat_app):
12501275
Other
12511276
================================================================================
12521277
alias Manage aliases
1253-
help List available commands with "help" or detailed help with "help cmd"
1278+
help List available commands or provide detailed help for a specific command
12541279
history View, run, edit, save, or clear previously entered commands
1255-
load Runs commands in script file that is encoded as either ASCII or UTF-8 text
1280+
load Run commands in script file that is encoded as either ASCII or UTF-8 text
12561281
macro Manage macros
1257-
py Invoke python command, shell, or script
1258-
pyscript Runs a python script file inside the console
1259-
quit Exits this application
1260-
set Sets a settable parameter or shows current settings of parameters
1282+
py Invoke Python command or shell
1283+
pyscript Run a Python script file inside the console
1284+
quit Exit this application
1285+
set Set a settable parameter or show current settings of parameters
12611286
shell Execute a command as if at the OS prompt
1262-
shortcuts Lists shortcuts available
1287+
shortcuts List available shortcuts
12631288
12641289
Undocumented commands:
12651290
======================
@@ -1556,15 +1581,15 @@ def test_is_text_file_bad_input(base_app):
15561581

15571582
def test_eof(base_app):
15581583
# Only thing to verify is that it returns True
1559-
assert base_app.do_eof('dont care')
1584+
assert base_app.do_eof('')
15601585

15611586
def test_eos(base_app):
15621587
sdir = 'dummy_dir'
15631588
base_app._script_dir.append(sdir)
15641589
assert len(base_app._script_dir) == 1
15651590

15661591
# Assert that it does NOT return true
1567-
assert not base_app.do_eos('dont care')
1592+
assert not base_app.do_eos('')
15681593

15691594
# And make sure it reduced the length of the script dir list
15701595
assert len(base_app._script_dir) == 0
@@ -1803,7 +1828,7 @@ def test_alias_create(base_app, capsys):
18031828
# Use the alias
18041829
run_cmd(base_app, 'fake')
18051830
out, err = capsys.readouterr()
1806-
assert "pyscript command requires at least 1 argument" in err
1831+
assert "the following arguments are required: script_path" in err
18071832

18081833
# See a list of aliases
18091834
out = run_cmd(base_app, 'alias list')
@@ -1905,7 +1930,7 @@ def test_macro_create(base_app, capsys):
19051930
# Use the macro
19061931
run_cmd(base_app, 'fake')
19071932
out, err = capsys.readouterr()
1908-
assert "pyscript command requires at least 1 argument" in err
1933+
assert "the following arguments are required: script_path" in err
19091934

19101935
# See a list of macros
19111936
out = run_cmd(base_app, 'macro list')

0 commit comments

Comments
 (0)