Skip to content

Commit 1c1da02

Browse files
committed
Changed some unit tests to use pytest-mock instead of mocker/monkeypatch because they were failing for me.
Added detection of ==SUPPRESS== in subcommand group names to avoid printing it in the help hint. Added some examples to tab_autocompletion to demonstrate how to tie in to cmd2 path_complete
1 parent f44871b commit 1c1da02

File tree

4 files changed

+43
-19
lines changed

4 files changed

+43
-19
lines changed

cmd2/argparse_completer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,10 @@ def _print_action_help(self, action: argparse.Action) -> None:
565565

566566
prefix = '{}{}'.format(flags, param)
567567
else:
568-
prefix = '{}'.format(str(action.dest).upper())
568+
if action.dest != SUPPRESS:
569+
prefix = '{}'.format(str(action.dest).upper())
570+
else:
571+
prefix = ''
569572

570573
prefix = ' {0: <{width}} '.format(prefix, width=20)
571574
pref_len = len(prefix)

cmd2/pyscript_bridge.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def process_flag(action, value):
230230
if action.option_strings:
231231
cmd_str[0] += '{} '.format(action.option_strings[0])
232232

233-
if isinstance(value, List) or isinstance(value, Tuple):
233+
if isinstance(value, List) or isinstance(value, tuple):
234234
for item in value:
235235
item = str(item).strip()
236236
if ' ' in item:
@@ -250,7 +250,7 @@ def traverse_parser(parser):
250250
cmd_str[0] += '{} '.format(self._args[action.dest])
251251
traverse_parser(action.choices[self._args[action.dest]])
252252
elif isinstance(action, argparse._AppendAction):
253-
if isinstance(self._args[action.dest], List) or isinstance(self._args[action.dest], Tuple):
253+
if isinstance(self._args[action.dest], list) or isinstance(self._args[action.dest], tuple):
254254
for values in self._args[action.dest]:
255255
process_flag(action, values)
256256
else:

examples/tab_autocompletion.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,17 +235,22 @@ def _do_vid_media_shows(self, args) -> None:
235235
actor_action = vid_movies_add_parser.add_argument('actor', help='Actors', nargs='*')
236236

237237
vid_movies_load_parser = vid_movies_commands_subparsers.add_parser('load')
238-
movie_file_action = vid_movies_load_parser.add_argument('movie_file', help='Movie database')
238+
vid_movie_file_action = vid_movies_load_parser.add_argument('movie_file', help='Movie database')
239+
240+
vid_movies_read_parser = vid_movies_commands_subparsers.add_parser('read')
241+
vid_movie_fread_action = vid_movies_read_parser.add_argument('movie_file', help='Movie database')
239242

240243
# tag the action objects with completion providers. This can be a collection or a callable
241244
setattr(director_action, argparse_completer.ACTION_ARG_CHOICES, static_list_directors)
242245
setattr(actor_action, argparse_completer.ACTION_ARG_CHOICES, 'instance_query_actors')
243246

244247
# tag the file property with a custom completion function 'delimeter_complete' provided by cmd2.
245-
setattr(movie_file_action, argparse_completer.ACTION_ARG_CHOICES,
248+
setattr(vid_movie_file_action, argparse_completer.ACTION_ARG_CHOICES,
246249
('delimiter_complete',
247250
{'delimiter': '/',
248251
'match_against': file_list}))
252+
setattr(vid_movie_fread_action, argparse_completer.ACTION_ARG_CHOICES,
253+
('path_complete', [False, False]))
249254

250255
vid_movies_delete_parser = vid_movies_commands_subparsers.add_parser('delete')
251256

@@ -324,6 +329,9 @@ def _do_media_shows(self, args) -> None:
324329

325330
movies_delete_parser = movies_commands_subparsers.add_parser('delete')
326331

332+
movies_load_parser = movies_commands_subparsers.add_parser('load')
333+
movie_file_action = movies_load_parser.add_argument('movie_file', help='Movie database')
334+
327335
shows_parser = media_types_subparsers.add_parser('shows')
328336
shows_parser.set_defaults(func=_do_media_shows)
329337

@@ -351,7 +359,8 @@ def do_media(self, args):
351359
def complete_media(self, text, line, begidx, endidx):
352360
""" Adds tab completion to media"""
353361
choices = {'actor': query_actors, # function
354-
'director': TabCompleteExample.static_list_directors # static list
362+
'director': TabCompleteExample.static_list_directors, # static list
363+
'movie_file': (self.path_complete, [False, False])
355364
}
356365
completer = argparse_completer.AutoCompleter(TabCompleteExample.media_parser, arg_choices=choices)
357366

tests/test_cmd2.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -406,21 +406,26 @@ def test_history_output_file(base_app):
406406
content = normalize(f.read())
407407
assert content == expected
408408

409-
def test_history_edit(base_app, monkeypatch):
409+
def test_history_edit(base_app, mock):
410410
# Set a fake editor just to make sure we have one. We aren't really
411411
# going to call it due to the mock
412412
base_app.editor = 'fooedit'
413413

414414
# Mock out the os.system call so we don't actually open an editor
415-
m = mock.MagicMock(name='system')
416-
monkeypatch.setattr("os.system", m)
415+
count = [0]
416+
417+
def fake_system(*args, **kwargs):
418+
count[0] += 1
419+
420+
mock.patch.object(os, 'system', fake_system)
417421

418422
# Run help command just so we have a command in history
419423
run_cmd(base_app, 'help')
420424
run_cmd(base_app, 'history -e 1')
421425

422426
# We have an editor, so should expect a system call
423-
m.assert_called_once()
427+
assert count[0] == 1
428+
424429

425430
def test_history_run_all_commands(base_app):
426431
# make sure we refuse to run all commands as a default
@@ -819,29 +824,36 @@ def test_edit_file_with_spaces(base_app, request, monkeypatch):
819824
# We think we have an editor, so should expect a system call
820825
m.assert_called_once_with('"{}" "{}"'.format(base_app.editor, filename))
821826

822-
def test_edit_blank(base_app, monkeypatch):
827+
def test_edit_blank(base_app, mock):
823828
# Set a fake editor just to make sure we have one. We aren't really going to call it due to the mock
824829
base_app.editor = 'fooedit'
825830

826-
# Mock out the os.system call so we don't actually open an editor
827-
m = mock.MagicMock(name='system')
828-
monkeypatch.setattr("os.system", m)
831+
count = [0]
832+
833+
def fake_system(*args, **kwargs):
834+
count[0] += 1
835+
836+
mock.patch.object(os, 'system', fake_system)
829837

830838
run_cmd(base_app, 'edit')
831839

832840
# We have an editor, so should expect a system call
833-
m.assert_called_once()
841+
assert count[0] == 1
834842

835843

836-
def test_base_py_interactive(base_app):
844+
def test_base_py_interactive(base_app, mock):
837845
# Mock out the InteractiveConsole.interact() call so we don't actually wait for a user's response on stdin
838-
m = mock.MagicMock(name='interact')
839-
InteractiveConsole.interact = m
846+
count = [0]
847+
848+
def fake_interact(*args, **kwargs):
849+
count[0] += 1
850+
851+
mock.patch.object(InteractiveConsole, 'interact', fake_interact)
840852

841853
run_cmd(base_app, "py")
842854

843855
# Make sure our mock was called once and only once
844-
m.assert_called_once()
856+
assert count[0] == 1
845857

846858

847859
def test_exclude_from_history(base_app, monkeypatch):

0 commit comments

Comments
 (0)