@@ -369,18 +369,6 @@ def __init__(self, completekey: str='tab', stdin=None, stdout=None, persistent_h
369369 except AttributeError :
370370 pass
371371
372- # If persistent readline history is enabled, then read history from file and register to write to file at exit
373- if persistent_history_file and rl_type != RlType .NONE :
374- persistent_history_file = os .path .expanduser (persistent_history_file )
375- try :
376- readline .read_history_file (persistent_history_file )
377- # default history len is -1 (infinite), which may grow unruly
378- readline .set_history_length (persistent_history_length )
379- except FileNotFoundError :
380- pass
381- import atexit
382- atexit .register (readline .write_history_file , persistent_history_file )
383-
384372 # Call super class constructor
385373 super ().__init__ (completekey = completekey , stdin = stdin , stdout = stdout )
386374
@@ -448,6 +436,37 @@ def __init__(self, completekey: str='tab', stdin=None, stdout=None, persistent_h
448436 # If this string is non-empty, then this warning message will print if a broken pipe error occurs while printing
449437 self .broken_pipe_warning = ''
450438
439+ # Check if history should persist
440+ if persistent_history_file and rl_type != RlType .NONE :
441+ persistent_history_file = os .path .expanduser (persistent_history_file )
442+ read_err = False
443+
444+ try :
445+ # First try to read any existing history file
446+ readline .read_history_file (persistent_history_file )
447+ except FileNotFoundError :
448+ pass
449+ except OSError as ex :
450+ self .perror ("readline cannot read persistent history file '{}': {}" .format (persistent_history_file , ex ),
451+ traceback_war = False )
452+ read_err = True
453+
454+ if not read_err :
455+ try :
456+ # Make sure readline is able to write the history file. Doing it this way is a more thorough check
457+ # than trying to open the file with write access since readline's underlying function needs to
458+ # create a temporary file in the same directory and may not have permission.
459+ readline .set_history_length (persistent_history_length )
460+ readline .write_history_file (persistent_history_file )
461+ except OSError as ex :
462+ self .perror ("readline cannot write persistent history file '{}': {}" .
463+ format (persistent_history_file , ex ), traceback_war = False )
464+ else :
465+ # Set history file and register to save our history at exit
466+ import atexit
467+ self .persistent_history_file = persistent_history_file
468+ atexit .register (readline .write_history_file , self .persistent_history_file )
469+
451470 # If a startup script is provided, then add it in the queue to load
452471 if startup_script is not None :
453472 startup_script = os .path .expanduser (startup_script )
@@ -610,7 +629,7 @@ def ppaged(self, msg: str, end: str='\n', chop: bool=False) -> None:
610629 try :
611630 self .pipe_proc .stdin .write (msg_str .encode ('utf-8' , 'replace' ))
612631 self .pipe_proc .stdin .close ()
613- except (IOError , KeyboardInterrupt ):
632+ except (OSError , KeyboardInterrupt ):
614633 pass
615634
616635 # Less doesn't respect ^C, but catches it for its own UI purposes (aborting search etc. inside less)
@@ -2574,8 +2593,9 @@ def run(filename):
25742593 try :
25752594 with open (filename ) as f :
25762595 interp .runcode (f .read ())
2577- except IOError as e :
2578- self .perror (e )
2596+ except OSError as ex :
2597+ error_msg = "Error opening script file '{}': {}" .format (filename , ex )
2598+ self .perror (error_msg , traceback_war = False )
25792599
25802600 bridge = PyscriptBridge (self )
25812601 self .pystate ['run' ] = run
@@ -2769,6 +2789,7 @@ def load_ipy(app):
27692789 history_parser_group .add_argument ('-s' , '--script' , action = 'store_true' , help = 'script format; no separation lines' )
27702790 history_parser_group .add_argument ('-o' , '--output-file' , metavar = 'FILE' , help = 'output commands to a script file' )
27712791 history_parser_group .add_argument ('-t' , '--transcript' , help = 'output commands and results to a transcript file' )
2792+ history_parser_group .add_argument ('-c' , '--clear' , action = "store_true" , help = 'clears all history' )
27722793 _history_arg_help = """empty all history items
27732794a one history item by number
27742795a..b, a:b, a:, ..b items by indices (inclusive)
@@ -2778,7 +2799,18 @@ def load_ipy(app):
27782799
27792800 @with_argparser (history_parser )
27802801 def do_history (self , args : argparse .Namespace ) -> None :
2781- """View, run, edit, and save previously entered commands."""
2802+ """View, run, edit, save, or clear previously entered commands."""
2803+
2804+ if args .clear :
2805+ # Clear command and readline history
2806+ self .history .clear ()
2807+
2808+ if rl_type != RlType .NONE :
2809+ readline .clear_history ()
2810+ if self .persistent_history_file :
2811+ os .remove (self .persistent_history_file )
2812+ return
2813+
27822814 # If an argument was supplied, then retrieve partial contents of the history
27832815 cowardly_refuse_to_run = False
27842816 if args .arg :
@@ -2984,25 +3016,30 @@ def do_load(self, arglist: List[str]) -> None:
29843016 """
29853017 # If arg is None or arg is an empty string this is an error
29863018 if not arglist :
2987- self .perror ('load command requires a file path: ' , traceback_war = False )
3019+ self .perror ('load command requires a file path' , traceback_war = False )
29883020 return
29893021
29903022 file_path = arglist [0 ].strip ()
29913023 expanded_path = os .path .abspath (os .path .expanduser (file_path ))
29923024
3025+ # Make sure the path exists and we can access it
3026+ if not os .path .exists (expanded_path ):
3027+ self .perror ("'{}' does not exist or cannot be accessed" .format (expanded_path ), traceback_war = False )
3028+ return
3029+
29933030 # Make sure expanded_path points to a file
29943031 if not os .path .isfile (expanded_path ):
2995- self .perror ('{} does not exist or is not a file' .format (expanded_path ), traceback_war = False )
3032+ self .perror (" '{}' is not a file" .format (expanded_path ), traceback_war = False )
29963033 return
29973034
29983035 # Make sure the file is not empty
29993036 if os .path .getsize (expanded_path ) == 0 :
3000- self .perror ('{} is empty' .format (expanded_path ), traceback_war = False )
3037+ self .perror (" '{}' is empty" .format (expanded_path ), traceback_war = False )
30013038 return
30023039
30033040 # Make sure the file is ASCII or UTF-8 encoded text
30043041 if not utils .is_text_file (expanded_path ):
3005- self .perror ('{} is not an ASCII or UTF-8 encoded text file' .format (expanded_path ), traceback_war = False )
3042+ self .perror (" '{}' is not an ASCII or UTF-8 encoded text file" .format (expanded_path ), traceback_war = False )
30063043 return
30073044
30083045 try :
@@ -3011,8 +3048,8 @@ def do_load(self, arglist: List[str]) -> None:
30113048 # self._script_dir list when done.
30123049 with open (expanded_path , encoding = 'utf-8' ) as target :
30133050 self .cmdqueue = target .read ().splitlines () + ['eos' ] + self .cmdqueue
3014- except IOError as e : # pragma: no cover
3015- self .perror (' Problem accessing script from {}: \n {}' .format (expanded_path , e ))
3051+ except OSError as ex : # pragma: no cover
3052+ self .perror (" Problem accessing script from '{}': {}" .format (expanded_path , ex ))
30163053 return
30173054
30183055 self ._script_dir .append (os .path .dirname (expanded_path ))
0 commit comments