diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-07-11 02:40:26 -0400 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2018-07-11 02:40:26 -0400 |
commit | 969dab29bbaed194edce881d60643f5e95a02366 (patch) | |
tree | 5db01e9916dde5da64db004a6b063e406a545da2 /cmd2/cmd2.py | |
parent | 6ddb6842e5ac87fb5c433eb8d86df48f3e045da2 (diff) | |
download | cmd2-git-969dab29bbaed194edce881d60643f5e95a02366.tar.gz |
Added --clear argument to history command
Added better error checking when loading readline history file
Improved some error messages
Changed IOError usages to OSError since they were merged in Python 3.3.
Diffstat (limited to 'cmd2/cmd2.py')
-rw-r--r-- | cmd2/cmd2.py | 81 |
1 files changed, 59 insertions, 22 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 42e00c39..bdb8452a 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -369,18 +369,6 @@ class Cmd(cmd.Cmd): except AttributeError: pass - # If persistent readline history is enabled, then read history from file and register to write to file at exit - if persistent_history_file and rl_type != RlType.NONE: - persistent_history_file = os.path.expanduser(persistent_history_file) - try: - readline.read_history_file(persistent_history_file) - # default history len is -1 (infinite), which may grow unruly - readline.set_history_length(persistent_history_length) - except FileNotFoundError: - pass - import atexit - atexit.register(readline.write_history_file, persistent_history_file) - # Call super class constructor super().__init__(completekey=completekey, stdin=stdin, stdout=stdout) @@ -448,6 +436,37 @@ class Cmd(cmd.Cmd): # If this string is non-empty, then this warning message will print if a broken pipe error occurs while printing self.broken_pipe_warning = '' + # Check if history should persist + if persistent_history_file and rl_type != RlType.NONE: + persistent_history_file = os.path.expanduser(persistent_history_file) + read_err = False + + try: + # First try to read any existing history file + readline.read_history_file(persistent_history_file) + except FileNotFoundError: + pass + except OSError as ex: + self.perror("readline cannot read persistent history file '{}': {}".format(persistent_history_file, ex), + traceback_war=False) + read_err = True + + if not read_err: + try: + # Make sure readline is able to write the history file. Doing it this way is a more thorough check + # than trying to open the file with write access since readline's underlying function needs to + # create a temporary file in the same directory and may not have permission. + readline.set_history_length(persistent_history_length) + readline.write_history_file(persistent_history_file) + except OSError as ex: + self.perror("readline cannot write persistent history file '{}': {}". + format(persistent_history_file, ex), traceback_war=False) + else: + # Set history file and register to save our history at exit + import atexit + self.persistent_history_file = persistent_history_file + atexit.register(readline.write_history_file, self.persistent_history_file) + # If a startup script is provided, then add it in the queue to load if startup_script is not None: startup_script = os.path.expanduser(startup_script) @@ -610,7 +629,7 @@ class Cmd(cmd.Cmd): try: self.pipe_proc.stdin.write(msg_str.encode('utf-8', 'replace')) self.pipe_proc.stdin.close() - except (IOError, KeyboardInterrupt): + except (OSError, KeyboardInterrupt): pass # Less doesn't respect ^C, but catches it for its own UI purposes (aborting search etc. inside less) @@ -2569,8 +2588,9 @@ Usage: Usage: unalias [-a] name [name ...] try: with open(filename) as f: interp.runcode(f.read()) - except IOError as e: - self.perror(e) + except OSError as ex: + error_msg = "Error opening script file '{}': {}".format(filename, ex) + self.perror(error_msg, traceback_war=False) bridge = PyscriptBridge(self) self.pystate['run'] = run @@ -2764,6 +2784,7 @@ Paths or arguments that contain spaces must be enclosed in quotes history_parser_group.add_argument('-s', '--script', action='store_true', help='script format; no separation lines') history_parser_group.add_argument('-o', '--output-file', metavar='FILE', help='output commands to a script file') history_parser_group.add_argument('-t', '--transcript', help='output commands and results to a transcript file') + history_parser_group.add_argument('-c', '--clear', action="store_true", help='clears all history') _history_arg_help = """empty all history items a one history item by number a..b, a:b, a:, ..b items by indices (inclusive) @@ -2773,7 +2794,18 @@ a..b, a:b, a:, ..b items by indices (inclusive) @with_argparser(history_parser) def do_history(self, args: argparse.Namespace) -> None: - """View, run, edit, and save previously entered commands.""" + """View, run, edit, save, or clear previously entered commands.""" + + if args.clear: + # Clear command and readline history + self.history.clear() + + if rl_type != RlType.NONE: + readline.clear_history() + if self.persistent_history_file: + os.remove(self.persistent_history_file) + return + # If an argument was supplied, then retrieve partial contents of the history cowardly_refuse_to_run = False if args.arg: @@ -2979,25 +3011,30 @@ Script should contain one command per line, just like command would be typed in """ # If arg is None or arg is an empty string this is an error if not arglist: - self.perror('load command requires a file path:', traceback_war=False) + self.perror('load command requires a file path', traceback_war=False) return file_path = arglist[0].strip() expanded_path = os.path.abspath(os.path.expanduser(file_path)) + # Make sure the path exists and we can access it + if not os.path.exists(expanded_path): + self.perror("'{}' does not exist or cannot be accessed".format(expanded_path), traceback_war=False) + return + # Make sure expanded_path points to a file if not os.path.isfile(expanded_path): - self.perror('{} does not exist or is not a file'.format(expanded_path), traceback_war=False) + self.perror("'{}' is not a file".format(expanded_path), traceback_war=False) return # Make sure the file is not empty if os.path.getsize(expanded_path) == 0: - self.perror('{} is empty'.format(expanded_path), traceback_war=False) + self.perror("'{}' is empty".format(expanded_path), traceback_war=False) return # Make sure the file is ASCII or UTF-8 encoded text if not utils.is_text_file(expanded_path): - self.perror('{} is not an ASCII or UTF-8 encoded text file'.format(expanded_path), traceback_war=False) + self.perror("'{}' is not an ASCII or UTF-8 encoded text file".format(expanded_path), traceback_war=False) return try: @@ -3006,8 +3043,8 @@ Script should contain one command per line, just like command would be typed in # self._script_dir list when done. with open(expanded_path, encoding='utf-8') as target: self.cmdqueue = target.read().splitlines() + ['eos'] + self.cmdqueue - except IOError as e: # pragma: no cover - self.perror('Problem accessing script from {}:\n{}'.format(expanded_path, e)) + except OSError as ex: # pragma: no cover + self.perror("Problem accessing script from '{}': {}".format(expanded_path, ex)) return self._script_dir.append(os.path.dirname(expanded_path)) |