diff options
-rw-r--r-- | cmd2/cmd2.py | 55 | ||||
-rwxr-xr-x | cmd2/parsing.py | 2 | ||||
-rw-r--r-- | docs/api/index.rst | 2 | ||||
-rw-r--r-- | docs/features/argument_processing.rst | 4 | ||||
-rw-r--r-- | docs/features/hooks.rst | 2 | ||||
-rwxr-xr-x | tests/test_history.py | 10 | ||||
-rw-r--r-- | tests/test_transcript.py | 27 | ||||
-rw-r--r-- | tests_isolated/test_commandset/test_commandset.py | 2 |
8 files changed, 46 insertions, 58 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index d2133ca4..7c221e40 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -2655,7 +2655,7 @@ class Cmd(cmd.Cmd): # Use line buffering new_stdout = open(utils.strip_quotes(statement.output_to), mode=mode, buffering=1) except OSError as ex: - raise RedirectionError('Failed to redirect because - {}'.format(ex)) + raise RedirectionError(f'Failed to redirect because: {ex}') redir_saved_state.redirecting = True sys.stdout = self.stdout = new_stdout @@ -4288,13 +4288,13 @@ class Cmd(cmd.Cmd): # -v must be used alone with no other options if args.verbose: if args.clear or args.edit or args.output_file or args.run or args.transcript or args.expanded or args.script: - self.poutput("-v can not be used with any other options") + self.poutput("-v cannot be used with any other options") self.poutput(self.history_parser.format_usage()) return # -s and -x can only be used if none of these options are present: [-c -r -e -o -t] if (args.script or args.expanded) and (args.clear or args.edit or args.output_file or args.run or args.transcript): - self.poutput("-s and -x can not be used with -c, -r, -e, -o, or -t") + self.poutput("-s and -x cannot be used with -c, -r, -e, -o, or -t") self.poutput(self.history_parser.format_usage()) return @@ -4308,7 +4308,7 @@ class Cmd(cmd.Cmd): except FileNotFoundError: pass except OSError as ex: - self.pexcept("Error removing history file '{}': {}".format(self.persistent_history_file, ex)) + self.perror(f"Error removing history file '{self.persistent_history_file}': {ex}") return if rl_type != RlType.NONE: @@ -4331,9 +4331,9 @@ class Cmd(cmd.Cmd): with os.fdopen(fd, 'w') as fobj: for command in history.values(): if command.statement.multiline_command: - fobj.write('{}\n'.format(command.expanded)) + fobj.write(f'{command.expanded}\n') else: - fobj.write('{}\n'.format(command.raw)) + fobj.write(f'{command.raw}\n') try: self._run_editor(fname) # noinspection PyTypeChecker @@ -4341,18 +4341,19 @@ class Cmd(cmd.Cmd): finally: os.remove(fname) elif args.output_file: + full_path = os.path.abspath(os.path.expanduser(args.output_file)) try: - with open(os.path.expanduser(args.output_file), 'w') as fobj: + with open(full_path, 'w') as fobj: for item in history.values(): if item.statement.multiline_command: - fobj.write('{}\n'.format(item.expanded)) + fobj.write(f"{item.expanded}\n") else: - fobj.write('{}\n'.format(item.raw)) + fobj.write(f"{item.raw}\n") plural = 's' if len(history) > 1 else '' - except OSError as e: - self.pexcept('Error saving {!r} - {}'.format(args.output_file, e)) + except OSError as ex: + self.perror(f"Error saving history file '{full_path}': {ex}") else: - self.pfeedback('{} command{} saved to {}'.format(len(history), plural, args.output_file)) + self.pfeedback(f"{len(history)} command{plural} saved to {full_path}") elif args.transcript: self._generate_transcript(list(history.values()), args.transcript) else: @@ -4404,11 +4405,10 @@ class Cmd(cmd.Cmd): hist_file = os.path.abspath(os.path.expanduser(hist_file)) - # on Windows, trying to open a directory throws a permission + # On Windows, trying to open a directory throws a permission # error, not a `IsADirectoryError`. So we'll check it ourselves. if os.path.isdir(hist_file): - msg = "Persistent history file '{}' is a directory" - self.perror(msg.format(hist_file)) + self.perror(f"Persistent history file '{hist_file}' is a directory") return # Create the directory for the history file if it doesn't already exist @@ -4416,8 +4416,7 @@ class Cmd(cmd.Cmd): try: os.makedirs(hist_file_dir, exist_ok=True) except OSError as ex: - msg = "Error creating persistent history file directory '{}': {}".format(hist_file_dir, ex) - self.pexcept(msg) + self.perror(f"Error creating persistent history file directory '{hist_file_dir}': {ex}") return # first we try and unpickle the history file @@ -4439,8 +4438,7 @@ class Cmd(cmd.Cmd): # If any of these errors occur when attempting to unpickle, just use an empty history pass except OSError as ex: - msg = "Can not read persistent history file '{}': {}" - self.pexcept(msg.format(hist_file, ex)) + self.perror(f"Cannot read persistent history file '{hist_file}': {ex}") return self.history = history @@ -4476,8 +4474,7 @@ class Cmd(cmd.Cmd): with open(self.persistent_history_file, 'wb') as fobj: pickle.dump(self.history, fobj) except OSError as ex: - msg = "Can not write persistent history file '{}': {}" - self.pexcept(msg.format(self.persistent_history_file, ex)) + self.perror(f"Cannot write persistent history file '{self.persistent_history_file}': {ex}") def _generate_transcript(self, history: List[Union[HistoryItem, str]], transcript_file: str) -> None: """Generate a transcript file from a given history of commands""" @@ -4485,7 +4482,7 @@ class Cmd(cmd.Cmd): transcript_path = os.path.abspath(os.path.expanduser(transcript_file)) transcript_dir = os.path.dirname(transcript_path) if not os.path.isdir(transcript_dir) or not os.access(transcript_dir, os.W_OK): - self.perror("{!r} is not a directory or you don't have write access".format(transcript_dir)) + self.perror(f"'{transcript_dir}' is not a directory or you don't have write access") return commands_run = 0 @@ -4515,10 +4512,10 @@ class Cmd(cmd.Cmd): history_item = history_item.raw for line in history_item.splitlines(): if first: - command += '{}{}\n'.format(self.prompt, line) + command += f"{self.prompt}{line}\n" first = False else: - command += '{}{}\n'.format(self.continuation_prompt, line) + command += f"{self.continuation_prompt}{line}\n" transcript += command # Use a StdSim object to capture output @@ -4547,23 +4544,21 @@ class Cmd(cmd.Cmd): # Check if all commands ran if commands_run < len(history): - warning = "Command {} triggered a stop and ended transcript generation early".format(commands_run) - self.pwarning(warning) + self.pwarning(f"Command {commands_run} triggered a stop and ended transcript generation early") # finally, we can write the transcript out to the file try: - with open(transcript_file, 'w') as fout: + with open(transcript_path, 'w') as fout: fout.write(transcript) except OSError as ex: - self.pexcept('Failed to save transcript: {}'.format(ex)) + self.perror(f"Error saving transcript file '{transcript_path}': {ex}") else: # and let the user know what we did if commands_run > 1: plural = 'commands and their outputs' else: plural = 'command and its output' - msg = '{} {} saved to transcript file {!r}' - self.pfeedback(msg.format(commands_run, plural, transcript_file)) + self.pfeedback(f"{commands_run} {plural} saved to transcript file '{transcript_path}'") edit_description = ( "Run a text editor and optionally open a file with it\n" diff --git a/cmd2/parsing.py b/cmd2/parsing.py index 3dff2689..db5dbd92 100755 --- a/cmd2/parsing.py +++ b/cmd2/parsing.py @@ -296,7 +296,7 @@ class StatementParser: def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> Tuple[bool, str]: """Determine whether a word is a valid name for a command. - Commands can not include redirection characters, whitespace, + Commands cannot include redirection characters, whitespace, or termination characters. They also cannot start with a shortcut. diff --git a/docs/api/index.rst b/docs/api/index.rst index 861c3f97..1f5e1625 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -5,7 +5,7 @@ These pages document the public API for ``cmd2``. If a method, class, function, attribute, or constant is not documented here, consider it private and subject to change. There are many classes, methods, functions, and constants in the source code which do not begin with an underscore but are not documented here. -When looking at the source code for this library, you can not safely assume +When looking at the source code for this library, you cannot safely assume that because something doesn't start with an underscore, it is a public API. If a release of this library changes any of the items documented here, the diff --git a/docs/features/argument_processing.rst b/docs/features/argument_processing.rst index bcc68633..9a78d720 100644 --- a/docs/features/argument_processing.rst +++ b/docs/features/argument_processing.rst @@ -170,7 +170,7 @@ To add additional text to the end of the generated help message, use the ``epilo from cmd2 import with_argparser argparser = argparse.ArgumentParser(description='create an html tag', - epilog='This command can not generate tags with no content, like <br/>.') + epilog='This command cannot generate tags with no content, like <br/>.') argparser.add_argument('tag', help='tag') argparser.add_argument('content', nargs='+', help='content to surround with tag') @with_argparser(argparser) @@ -193,7 +193,7 @@ Which yields: optional arguments: -h, --help show this help message and exit - This command can not generate tags with no content, like <br/> + This command cannot generate tags with no content, like <br/> .. warning:: diff --git a/docs/features/hooks.rst b/docs/features/hooks.rst index acf9dcc4..881b4e7e 100644 --- a/docs/features/hooks.rst +++ b/docs/features/hooks.rst @@ -207,7 +207,7 @@ with the :data:`~cmd2.plugin.PostparsingData.stop` attribute set to ``True``: Precommand Hooks ---------------- -Precommand hooks can modify the user input, but can not request the application +Precommand hooks can modify the user input, but cannot request the application terminate. If your hook needs to be able to exit the application, you should implement it as a postparsing hook. diff --git a/tests/test_history.py b/tests/test_history.py index 41ae2a9e..d1285705 100755 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -616,7 +616,7 @@ def test_history_verbose_with_other_options(base_app): for opt in options_to_test: out, err = run_cmd(base_app, 'history -v ' + opt) assert len(out) == 4 - assert out[0] == '-v can not be used with any other options' + assert out[0] == '-v cannot be used with any other options' assert out[1].startswith('Usage:') @@ -635,7 +635,7 @@ def test_history_script_with_invalid_options(base_app): for opt in options_to_test: out, err = run_cmd(base_app, 'history -s ' + opt) assert len(out) == 4 - assert out[0] == '-s and -x can not be used with -c, -r, -e, -o, or -t' + assert out[0] == '-s and -x cannot be used with -c, -r, -e, -o, or -t' assert out[1].startswith('Usage:') @@ -653,7 +653,7 @@ def test_history_expanded_with_invalid_options(base_app): for opt in options_to_test: out, err = run_cmd(base_app, 'history -x ' + opt) assert len(out) == 4 - assert out[0] == '-s and -x can not be used with -c, -r, -e, -o, or -t' + assert out[0] == '-s and -x cannot be used with -c, -r, -e, -o, or -t' assert out[1].startswith('Usage:') @@ -761,7 +761,7 @@ def test_history_file_permission_error(mocker, capsys): cmd2.Cmd(persistent_history_file='/tmp/doesntmatter') out, err = capsys.readouterr() assert not out - assert 'Can not read' in err + assert 'Cannot read' in err def test_history_file_conversion_no_truncate_on_init(hist_file, capsys): @@ -843,4 +843,4 @@ def test_persist_history_permission_error(hist_file, mocker, capsys): app._persist_history() out, err = capsys.readouterr() assert not out - assert 'Can not write' in err + assert 'Cannot write' in err diff --git a/tests/test_transcript.py b/tests/test_transcript.py index ccb28740..22e04239 100644 --- a/tests/test_transcript.py +++ b/tests/test_transcript.py @@ -184,31 +184,24 @@ this is a \/multiline\/ command assert xscript == expected -def test_history_transcript_bad_filename(): +def test_history_transcript_bad_path(mocker): app = CmdLineApp() app.stdout = StdSim(app.stdout) run_cmd(app, 'orate this is\na /multiline/\ncommand;\n') run_cmd(app, 'speak /tmp/file.txt is not a regex') - expected = r"""(Cmd) orate this is -> a /multiline/ -> command; -this is a \/multiline\/ command -(Cmd) speak /tmp/file.txt is not a regex -\/tmp\/file.txt is not a regex -""" - - # make a tmp file + # Bad directory history_fname = '~/fakedir/this_does_not_exist.txt' + out, err = run_cmd(app, 'history -t "{}"'.format(history_fname)) + assert "is not a directory" in err[0] - # tell the history command to create a transcript - run_cmd(app, 'history -t "{}"'.format(history_fname)) + # Cause os.open to fail and make sure error gets printed + mock_remove = mocker.patch('builtins.open') + mock_remove.side_effect = OSError - # read in the transcript created by the history command - with pytest.raises(FileNotFoundError): - with open(history_fname) as f: - transcript = f.read() - assert transcript == expected + history_fname = 'outfile.txt' + out, err = run_cmd(app, 'history -t "{}"'.format(history_fname)) + assert "Error saving transcript file" in err[0] def test_run_script_record_transcript(base_app, request): diff --git a/tests_isolated/test_commandset/test_commandset.py b/tests_isolated/test_commandset/test_commandset.py index 2e7d837f..c3c86458 100644 --- a/tests_isolated/test_commandset/test_commandset.py +++ b/tests_isolated/test_commandset/test_commandset.py @@ -157,7 +157,7 @@ def test_custom_construct_commandsets(): cmds_cats, cmds_doc, cmds_undoc, help_topics = app._build_command_info() assert 'Command Set B' in cmds_cats - # Verifies that the same CommandSet can not be loaded twice + # Verifies that the same CommandSet cannot be loaded twice command_set_2 = CommandSetB('bar') with pytest.raises(CommandSetRegistrationError): assert app.register_command_set(command_set_2) |