diff --git a/CHANGELOG.md b/CHANGELOG.md index d98b1497..56696a10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ add output to the operating system clipboard * Updated unit tests to be Python 3.12 compliant. * Fall back to bz2 compression of history file when lzma is not installed. + * Added settable called `save_scripted_commands` which determines whether to save commands + run in scripts and pyscripts to history. * Deletions (potentially breaking changes) * Removed `apply_style` from `Cmd.pwarning()`. diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index f162ef87..f0ee1498 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -322,6 +322,7 @@ def __init__( self.editor = Cmd.DEFAULT_EDITOR self.feedback_to_output = False # Do not include nonessentials in >, | output by default (things like timing) self.quiet = False # Do not suppress nonessential output + self.save_scripted_commands = True # Save commands run in scripts and pyscripts to history self.timing = False # Prints elapsed time for each command # The maximum number of CompletionItems to display during tab completion. If the number of completion @@ -1084,12 +1085,7 @@ def allow_style_type(value: str) -> ansi.AllowStyle: ) self.add_settable( - Settable( - 'always_show_hint', - bool, - 'Display tab completion hint even when completion suggestions print', - self, - ) + Settable('always_show_hint', bool, 'Display tab completion hint even when completion suggestions print', self) ) self.add_settable(Settable('debug', bool, "Show full traceback on exception", self)) self.add_settable(Settable('echo', bool, "Echo command issued into output", self)) @@ -1099,6 +1095,9 @@ def allow_style_type(value: str) -> ansi.AllowStyle: Settable('max_completion_items', int, "Maximum number of CompletionItems to display during tab completion", self) ) self.add_settable(Settable('quiet', bool, "Don't print nonessential feedback", self)) + self.add_settable( + Settable('save_scripted_commands', bool, 'Save commands run in scripts and pyscripts to history', self) + ) self.add_settable(Settable('timing', bool, "Report execution times", self)) # ----- Methods related to presenting output to the user ----- @@ -4955,7 +4954,13 @@ def _persist_history(self) -> None: except OSError as ex: self.perror(f"Cannot write persistent history file '{self.persistent_history_file}': {ex}") - def _generate_transcript(self, history: Union[List[HistoryItem], List[str]], transcript_file: str) -> None: + def _generate_transcript( + self, + history: Union[List[HistoryItem], List[str]], + transcript_file: str, + *, + add_to_history: bool = True, + ) -> None: """Generate a transcript file from a given history of commands""" self.last_result = False @@ -5005,7 +5010,11 @@ def _generate_transcript(self, history: Union[List[HistoryItem], List[str]], tra # then run the command and let the output go into our buffer try: - stop = self.onecmd_plus_hooks(history_item, raise_keyboard_interrupt=True) + stop = self.onecmd_plus_hooks( + history_item, + add_to_history=add_to_history, + raise_keyboard_interrupt=True, + ) except KeyboardInterrupt as ex: self.perror(ex) stop = True @@ -5149,9 +5158,17 @@ def do_run_script(self, args: argparse.Namespace) -> Optional[bool]: if args.transcript: # self.last_resort will be set by _generate_transcript() - self._generate_transcript(script_commands, os.path.expanduser(args.transcript)) + self._generate_transcript( + script_commands, + os.path.expanduser(args.transcript), + add_to_history=self.save_scripted_commands, + ) else: - stop = self.runcmds_plus_hooks(script_commands, stop_on_keyboard_interrupt=True) + stop = self.runcmds_plus_hooks( + script_commands, + add_to_history=self.save_scripted_commands, + stop_on_keyboard_interrupt=True, + ) self.last_result = True return stop diff --git a/cmd2/py_bridge.py b/cmd2/py_bridge.py index ab7b4001..2c60a3c0 100644 --- a/cmd2/py_bridge.py +++ b/cmd2/py_bridge.py @@ -126,7 +126,11 @@ def __call__(self, command: str, *, echo: Optional[bool] = None) -> CommandResul self._cmd2_app.stdout = cast(TextIO, copy_cmd_stdout) with redirect_stdout(cast(IO[str], copy_cmd_stdout)): with redirect_stderr(cast(IO[str], copy_stderr)): - stop = self._cmd2_app.onecmd_plus_hooks(command, py_bridge_call=True) + stop = self._cmd2_app.onecmd_plus_hooks( + command, + add_to_history=self._cmd2_app.save_scripted_commands, + py_bridge_call=True, + ) finally: with self._cmd2_app.sigint_protection: self._cmd2_app.stdout = cast(IO[str], copy_cmd_stdout.inner_stream) diff --git a/tests/conftest.py b/tests/conftest.py index dca3f70b..85506894 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -98,20 +98,21 @@ def verify_help_text( # Output from the set command SET_TXT = ( - "Name Value Description \n" - "==================================================================================================================\n" - "allow_style Terminal Allow ANSI text style sequences in output (valid values: \n" - " Always, Never, Terminal) \n" - "always_show_hint False Display tab completion hint even when completion suggestions\n" - " print \n" - "debug False Show full traceback on exception \n" - "echo False Echo command issued into output \n" - "editor vim Program used by 'edit' \n" - "feedback_to_output False Include nonessentials in '|', '>' results \n" - "max_completion_items 50 Maximum number of CompletionItems to display during tab \n" - " completion \n" - "quiet False Don't print nonessential feedback \n" - "timing False Report execution times \n" + "Name Value Description \n" + "====================================================================================================================\n" + "allow_style Terminal Allow ANSI text style sequences in output (valid values: \n" + " Always, Never, Terminal) \n" + "always_show_hint False Display tab completion hint even when completion suggestions\n" + " print \n" + "debug False Show full traceback on exception \n" + "echo False Echo command issued into output \n" + "editor vim Program used by 'edit' \n" + "feedback_to_output False Include nonessentials in '|', '>' results \n" + "max_completion_items 50 Maximum number of CompletionItems to display during tab \n" + " completion \n" + "quiet False Don't print nonessential feedback \n" + "save_scripted_commands True Save commands run in scripts and pyscripts to history \n" + "timing False Report execution times \n" ) diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index e179239b..8f6c027f 100755 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -442,6 +442,27 @@ def test_run_script_with_utf8_file(base_app, request): assert script_err == manual_err +def test_run_script_save_scripted_commands(base_app, request): + test_dir = os.path.dirname(request.module.__file__) + filename = os.path.join(test_dir, 'scripts', 'help.txt') + command = f'run_script {filename}' + + # Save scripted commands + base_app.save_scripted_commands = True + base_app.history.clear() + run_cmd(base_app, command) + assert len(base_app.history) == 2 + assert base_app.history.get(1).raw == command + assert base_app.history.get(2).raw == 'help -v' + + # Do not save scripted commands + base_app.save_scripted_commands = False + base_app.history.clear() + run_cmd(base_app, command) + assert len(base_app.history) == 1 + assert base_app.history.get(1).raw == command + + def test_run_script_nested_run_scripts(base_app, request): # Verify that running a script with nested run_script commands works correctly, # and runs the nested script commands in the correct order. diff --git a/tests/test_run_pyscript.py b/tests/test_run_pyscript.py index 16ea4a39..dd37a1e6 100644 --- a/tests/test_run_pyscript.py +++ b/tests/test_run_pyscript.py @@ -107,6 +107,27 @@ def test_run_pyscript_help(base_app, request): assert out1 and out1 == out2 +def test_run_pyscript_save_scripted_commands(base_app, request): + test_dir = os.path.dirname(request.module.__file__) + python_script = os.path.join(test_dir, 'pyscript', 'help.py') + command = f'run_pyscript {python_script}' + + # Save scripted commands + base_app.save_scripted_commands = True + base_app.history.clear() + run_cmd(base_app, command) + assert len(base_app.history) == 2 + assert base_app.history.get(1).raw == command + assert base_app.history.get(2).raw == 'help' + + # Do not save scripted commands + base_app.save_scripted_commands = False + base_app.history.clear() + run_cmd(base_app, command) + assert len(base_app.history) == 1 + assert base_app.history.get(1).raw == command + + def test_run_pyscript_dir(base_app, request): test_dir = os.path.dirname(request.module.__file__) python_script = os.path.join(test_dir, 'pyscript', 'pyscript_dir.py') diff --git a/tests/transcripts/regex_set.txt b/tests/transcripts/regex_set.txt index c2a0f091..994c098f 100644 --- a/tests/transcripts/regex_set.txt +++ b/tests/transcripts/regex_set.txt @@ -10,18 +10,19 @@ now: 'Terminal' editor - was: '/.*/' now: 'vim' (Cmd) set -Name Value Description/ +/ -================================================================================================================== -allow_style Terminal Allow ANSI text style sequences in output (valid values:/ +/ - Always, Never, Terminal)/ +/ -always_show_hint False Display tab completion hint even when completion suggestions - print/ +/ -debug False Show full traceback on exception/ +/ -echo False Echo command issued into output/ +/ -editor vim Program used by 'edit'/ +/ -feedback_to_output False Include nonessentials in '|', '>' results/ +/ -max_completion_items 50 Maximum number of CompletionItems to display during tab/ +/ - completion/ +/ -maxrepeats 3 Max number of `--repeat`s allowed/ +/ -quiet False Don't print nonessential feedback/ +/ -timing False Report execution times/ +/ +Name Value Description/ +/ +==================================================================================================================== +allow_style Terminal Allow ANSI text style sequences in output (valid values:/ +/ + Always, Never, Terminal)/ +/ +always_show_hint False Display tab completion hint even when completion suggestions + print/ +/ +debug False Show full traceback on exception/ +/ +echo False Echo command issued into output/ +/ +editor vim Program used by 'edit'/ +/ +feedback_to_output False Include nonessentials in '|', '>' results/ +/ +max_completion_items 50 Maximum number of CompletionItems to display during tab/ +/ + completion/ +/ +maxrepeats 3 Max number of `--repeat`s allowed/ +/ +quiet False Don't print nonessential feedback/ +/ +save_scripted_commands True Save commands run in scripts and pyscripts to history/ +/ +timing False Report execution times/ +/