@@ -369,18 +369,6 @@ def __init__(self, completekey: str='tab', stdin=None, stdout=None, persistent_h
369
369
except AttributeError :
370
370
pass
371
371
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
-
384
372
# Call super class constructor
385
373
super ().__init__ (completekey = completekey , stdin = stdin , stdout = stdout )
386
374
@@ -448,6 +436,37 @@ def __init__(self, completekey: str='tab', stdin=None, stdout=None, persistent_h
448
436
# If this string is non-empty, then this warning message will print if a broken pipe error occurs while printing
449
437
self .broken_pipe_warning = ''
450
438
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
+
451
470
# If a startup script is provided, then add it in the queue to load
452
471
if startup_script is not None :
453
472
startup_script = os .path .expanduser (startup_script )
@@ -610,7 +629,7 @@ def ppaged(self, msg: str, end: str='\n', chop: bool=False) -> None:
610
629
try :
611
630
self .pipe_proc .stdin .write (msg_str .encode ('utf-8' , 'replace' ))
612
631
self .pipe_proc .stdin .close ()
613
- except (IOError , KeyboardInterrupt ):
632
+ except (OSError , KeyboardInterrupt ):
614
633
pass
615
634
616
635
# 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):
2574
2593
try :
2575
2594
with open (filename ) as f :
2576
2595
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 )
2579
2599
2580
2600
bridge = PyscriptBridge (self )
2581
2601
self .pystate ['run' ] = run
@@ -2769,6 +2789,7 @@ def load_ipy(app):
2769
2789
history_parser_group .add_argument ('-s' , '--script' , action = 'store_true' , help = 'script format; no separation lines' )
2770
2790
history_parser_group .add_argument ('-o' , '--output-file' , metavar = 'FILE' , help = 'output commands to a script file' )
2771
2791
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' )
2772
2793
_history_arg_help = """empty all history items
2773
2794
a one history item by number
2774
2795
a..b, a:b, a:, ..b items by indices (inclusive)
@@ -2778,7 +2799,18 @@ def load_ipy(app):
2778
2799
2779
2800
@with_argparser (history_parser )
2780
2801
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
+
2782
2814
# If an argument was supplied, then retrieve partial contents of the history
2783
2815
cowardly_refuse_to_run = False
2784
2816
if args .arg :
@@ -2984,25 +3016,30 @@ def do_load(self, arglist: List[str]) -> None:
2984
3016
"""
2985
3017
# If arg is None or arg is an empty string this is an error
2986
3018
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 )
2988
3020
return
2989
3021
2990
3022
file_path = arglist [0 ].strip ()
2991
3023
expanded_path = os .path .abspath (os .path .expanduser (file_path ))
2992
3024
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
+
2993
3030
# Make sure expanded_path points to a file
2994
3031
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 )
2996
3033
return
2997
3034
2998
3035
# Make sure the file is not empty
2999
3036
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 )
3001
3038
return
3002
3039
3003
3040
# Make sure the file is ASCII or UTF-8 encoded text
3004
3041
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 )
3006
3043
return
3007
3044
3008
3045
try :
@@ -3011,8 +3048,8 @@ def do_load(self, arglist: List[str]) -> None:
3011
3048
# self._script_dir list when done.
3012
3049
with open (expanded_path , encoding = 'utf-8' ) as target :
3013
3050
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 ))
3016
3053
return
3017
3054
3018
3055
self ._script_dir .append (os .path .dirname (expanded_path ))
0 commit comments