diff options
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)) |