diff options
-rwxr-xr-x | cmd2.py | 52 | ||||
-rw-r--r-- | tests/conftest.py | 7 | ||||
-rw-r--r-- | tests/test_cmd2.py | 47 |
3 files changed, 93 insertions, 13 deletions
@@ -1697,14 +1697,14 @@ Paths or arguments that contain spaces must be enclosed in quotes embed(banner1=banner, exit_msg=exit_msg) history_parser = argparse.ArgumentParser( - description='run, edit, and save past commands', + description='run, edit, and save previously entered commands', formatter_class=argparse.RawTextHelpFormatter, ) - history_parser.add_argument('-s', '--script', action='store_true', help='script format; no separation lines') history_parser_group = history_parser.add_mutually_exclusive_group() history_parser_group.add_argument('-r', '--run', action='store_true', help='run selected history items') history_parser_group.add_argument('-e', '--edit', action='store_true', help='edit and then run selected history items') - history_parser_group.add_argument('-o', '--output-file', metavar='FILE', type=argparse.FileType('w'), help='output to file') + history_parser_group.add_argument('-o', '--output-file', metavar='FILE', help='output to file') + history_parser.add_argument('-s', '--script', action='store_true', help='script format; no separation lines') _history_arg_help = """empty all history items a one history item by number a..b, a:b, a:, ..b items by indices (inclusive) @@ -1714,9 +1714,12 @@ a..b, a:b, a:, ..b items by indices (inclusive) @with_argument_parser(history_parser) def do_history(self, args): - # If an argument was supplied, then retrieve partial contents of the history + # If an argument was supplied, then retrieve partial contents of the + # history + cowerdly_refuse_to_run = False if args.arg: - # If a character indicating a slice is present, retrieve a slice of the history + # If a character indicating a slice is present, retrieve + # a slice of the history arg = args.arg if '..' in arg or ':' in arg: try: @@ -1729,14 +1732,43 @@ a..b, a:b, a:, ..b items by indices (inclusive) history = self.history.get(arg) else: # If no arg given, then retrieve the entire history + cowerdly_refuse_to_run = True history = self.history - # Display the history items retrieved - for hi in history: - if args.script: - self.poutput(hi) + if args.run: + if cowerdly_refuse_to_run: + self.perror("Cowerdly refusing to run all previously entered commands.", traceback_war=False) + self.perror("If this is what you want to do, specify '1:' as the range of history.", traceback_war=False) else: - self.poutput(hi.pr()) + self.cmdqueue.extend(history) + elif args.edit: + fd, fname = tempfile.mkstemp(suffix='.txt', text=True) + with os.fdopen(fd, 'w') as fobj: + for cmd in history: + fobj.write('{}\n'.format(cmd)) + try: + os.system('"{}" "{}"'.format(self.editor, fname)) + self.do_load(fname) + except: + raise + finally: + os.remove(fname) + elif args.output_file: + try: + with open(os.path.expanduser(args.output_file), 'w') as fobj: + for cmd in history: + fobj.write('{}\n'.format(cmd)) + plural = 's' if len(history) > 1 else '' + self.pfeedback('{} command{} saved to {}'.format(len(history), plural, args.output_file)) + except Exception as e: + self.perror('Saving {!r} - {}'.format(args.output_file, e), traceback_war=False) + else: + # Display the history items retrieved + for hi in history: + if args.script: + self.poutput(hi) + else: + self.poutput(hi.pr()) def _last_matching(self, arg): """Return the last item from the history list that matches arg. Or if arg not provided, return last item. diff --git a/tests/conftest.py b/tests/conftest.py index e20d2511..5ecb6f82 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,9 +19,9 @@ edit help history load py pyscript quit run save set shell shortcuts """ # Help text for the history command -HELP_HISTORY = """usage: history [-h] [-s] [-r | -e | -o FILE] [arg] +HELP_HISTORY = """usage: history [-h] [-r | -e | -o FILE] [-s] [arg] -run, edit, and save past commands +run, edit, and save previously entered commands positional arguments: arg empty all history items @@ -32,11 +32,12 @@ positional arguments: optional arguments: -h, --help show this help message and exit - -s, --script script format; no separation lines -r, --run run selected history items -e, --edit edit and then run selected history items -o FILE, --output-file FILE output to file + -s, --script script format; no separation lines + """ # Output from the shortcuts command with default built-in shortcuts diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 0df3b80a..280df5ab 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -316,6 +316,53 @@ def test_history_with_span_index_error(base_app): """) assert out == expected +def test_history_output_file(base_app): + run_cmd(base_app, 'help') + run_cmd(base_app, 'shortcuts') + run_cmd(base_app, 'help history') + + fd, fname = tempfile.mkstemp(prefix='', suffix='.txt') + os.close(fd) + run_cmd(base_app, 'history -o "{}"'.format(fname)) + expected = normalize('\n'.join(['help', 'shortcuts', 'help history'])) + with open(fname) as f: + content = normalize(f.read()) + assert content == expected + +def test_history_edit(base_app, monkeypatch): + # Set a fake editor just to make sure we have one. We aren't really + # going to call it due to the mock + base_app.editor = 'fooedit' + + # Mock out the os.system call so we don't actually open an editor + m = mock.MagicMock(name='system') + monkeypatch.setattr("os.system", m) + + # Run help command just so we have a command in history + run_cmd(base_app, 'help') + run_cmd(base_app, 'edit') + + # We have an editor, so should expect a system call + m.assert_called_once() + +def test_history_run_all_commands(base_app): + # make sure we refuse to run all commands as a default + run_cmd(base_app, 'shortcuts') + out = run_cmd(base_app, 'history -r') + # this should generate an error, but we don't currently have a way to + # capture stderr in these tests. So we assume that if we got nothing on + # standard out, that the error occured because if the commaned executed + # then we should have a list of shortcuts in our output + assert out == [] + +def test_history_run_one_command(base_app): + run_cmd(base_app, 'help') + # because of the way run_cmd works, it will not + # process the cmdqueue. So we can check to see + # if the cmdqueue has a waiting item + run_cmd(base_app, 'history -r 1') + assert len(base_app.cmdqueue) == 1 + def test_base_load(base_app, request): test_dir = os.path.dirname(request.module.__file__) filename = os.path.join(test_dir, 'script.txt') |