@@ -358,8 +358,7 @@ def __init__(
358358 terminators = terminators , multiline_commands = multiline_commands , shortcuts = shortcuts
359359 )
360360
361- # Stores results from the last command run to enable usage of results in a Python script or interactive console
362- # Built-in commands don't make use of this. It is purely there for user-defined commands and convenience.
361+ # Stores results from the last command run to enable usage of results in a Python script or Python console
363362 self .last_result : Any = None
364363
365364 # Used by run_script command to store current script dir as a LIFO queue to support _relative_run_script command
@@ -3162,6 +3161,8 @@ def do_alias(self, args: argparse.Namespace) -> None:
31623161 @as_subcommand_to ('alias' , 'create' , alias_create_parser , help = alias_create_description .lower ())
31633162 def _alias_create (self , args : argparse .Namespace ) -> None :
31643163 """Create or overwrite an alias"""
3164+ self .last_result = False
3165+
31653166 # Validate the alias name
31663167 valid , errmsg = self .statement_parser .is_valid_command (args .name )
31673168 if not valid :
@@ -3191,6 +3192,7 @@ def _alias_create(self, args: argparse.Namespace) -> None:
31913192 self .poutput (f"Alias '{ args .name } ' { result } " )
31923193
31933194 self .aliases [args .name ] = value
3195+ self .last_result = True
31943196
31953197 # alias -> delete
31963198 alias_delete_help = "delete aliases"
@@ -3209,11 +3211,14 @@ def _alias_create(self, args: argparse.Namespace) -> None:
32093211 @as_subcommand_to ('alias' , 'delete' , alias_delete_parser , help = alias_delete_help )
32103212 def _alias_delete (self , args : argparse .Namespace ) -> None :
32113213 """Delete aliases"""
3214+ self .last_result = True
3215+
32123216 if args .all :
32133217 self .aliases .clear ()
32143218 self .poutput ("All aliases deleted" )
32153219 elif not args .names :
32163220 self .perror ("Either --all or alias name(s) must be specified" )
3221+ self .last_result = False
32173222 else :
32183223 for cur_name in utils .remove_duplicates (args .names ):
32193224 if cur_name in self .aliases :
@@ -3243,6 +3248,8 @@ def _alias_delete(self, args: argparse.Namespace) -> None:
32433248 @as_subcommand_to ('alias' , 'list' , alias_list_parser , help = alias_list_help )
32443249 def _alias_list (self , args : argparse .Namespace ) -> None :
32453250 """List some or all aliases as 'alias create' commands"""
3251+ self .last_result = {} # Dict[alias_name, alias_value]
3252+
32463253 tokens_to_quote = constants .REDIRECTION_TOKENS
32473254 tokens_to_quote .extend (self .statement_parser .terminators )
32483255
@@ -3268,6 +3275,7 @@ def _alias_list(self, args: argparse.Namespace) -> None:
32683275 val += ' ' + ' ' .join (command_args )
32693276
32703277 self .poutput (f"alias create { name } { val } " )
3278+ self .last_result [name ] = val
32713279
32723280 for name in not_found :
32733281 self .perror (f"Alias '{ name } ' not found" )
@@ -3346,6 +3354,8 @@ def do_macro(self, args: argparse.Namespace) -> None:
33463354 @as_subcommand_to ('macro' , 'create' , macro_create_parser , help = macro_create_help )
33473355 def _macro_create (self , args : argparse .Namespace ) -> None :
33483356 """Create or overwrite a macro"""
3357+ self .last_result = False
3358+
33493359 # Validate the macro name
33503360 valid , errmsg = self .statement_parser .is_valid_command (args .name )
33513361 if not valid :
@@ -3420,6 +3430,7 @@ def _macro_create(self, args: argparse.Namespace) -> None:
34203430 self .poutput (f"Macro '{ args .name } ' { result } " )
34213431
34223432 self .macros [args .name ] = Macro (name = args .name , value = value , minimum_arg_count = max_arg_num , arg_list = arg_list )
3433+ self .last_result = True
34233434
34243435 # macro -> delete
34253436 macro_delete_help = "delete macros"
@@ -3437,11 +3448,14 @@ def _macro_create(self, args: argparse.Namespace) -> None:
34373448 @as_subcommand_to ('macro' , 'delete' , macro_delete_parser , help = macro_delete_help )
34383449 def _macro_delete (self , args : argparse .Namespace ) -> None :
34393450 """Delete macros"""
3451+ self .last_result = True
3452+
34403453 if args .all :
34413454 self .macros .clear ()
34423455 self .poutput ("All macros deleted" )
34433456 elif not args .names :
34443457 self .perror ("Either --all or macro name(s) must be specified" )
3458+ self .last_result = False
34453459 else :
34463460 for cur_name in utils .remove_duplicates (args .names ):
34473461 if cur_name in self .macros :
@@ -3471,6 +3485,8 @@ def _macro_delete(self, args: argparse.Namespace) -> None:
34713485 @as_subcommand_to ('macro' , 'list' , macro_list_parser , help = macro_list_help )
34723486 def _macro_list (self , args : argparse .Namespace ) -> None :
34733487 """List some or all macros as 'macro create' commands"""
3488+ self .last_result = {} # Dict[macro_name, macro_value]
3489+
34743490 tokens_to_quote = constants .REDIRECTION_TOKENS
34753491 tokens_to_quote .extend (self .statement_parser .terminators )
34763492
@@ -3496,6 +3512,7 @@ def _macro_list(self, args: argparse.Namespace) -> None:
34963512 val += ' ' + ' ' .join (command_args )
34973513
34983514 self .poutput (f"macro create { name } { val } " )
3515+ self .last_result [name ] = val
34993516
35003517 for name in not_found :
35013518 self .perror (f"Macro '{ name } ' not found" )
@@ -3552,6 +3569,8 @@ def complete_help_subcommands(
35523569 @with_argparser (help_parser )
35533570 def do_help (self , args : argparse .Namespace ) -> None :
35543571 """List available commands or provide detailed help for a specific command"""
3572+ self .last_result = True
3573+
35553574 if not args .command or args .verbose :
35563575 self ._help_menu (args .verbose )
35573576
@@ -3586,6 +3605,7 @@ def do_help(self, args: argparse.Namespace) -> None:
35863605
35873606 # Set apply_style to False so help_error's style is not overridden
35883607 self .perror (err_msg , apply_style = False )
3608+ self .last_result = False
35893609
35903610 def print_topics (self , header : str , cmds : Optional [List [str ]], cmdlen : int , maxcol : int ) -> None :
35913611 """
@@ -3797,6 +3817,7 @@ def do_shortcuts(self, _: argparse.Namespace) -> None:
37973817 sorted_shortcuts = sorted (self .statement_parser .shortcuts , key = lambda x : self .default_sort_key (x [0 ]))
37983818 result = "\n " .join ('{}: {}' .format (sc [0 ], sc [1 ]) for sc in sorted_shortcuts )
37993819 self .poutput (f"Shortcuts for other commands:\n { result } " )
3820+ self .last_result = True
38003821
38013822 eof_parser = argparse_custom .DEFAULT_ARGUMENT_PARSER (
38023823 description = "Called when Ctrl-D is pressed" , epilog = INTERNAL_COMMAND_EPILOG
@@ -3810,6 +3831,7 @@ def do_eof(self, _: argparse.Namespace) -> Optional[bool]:
38103831 """
38113832 self .poutput ()
38123833
3834+ # self.last_result will be set by do_quit()
38133835 # noinspection PyTypeChecker
38143836 return self .do_quit ('' )
38153837
@@ -3819,6 +3841,7 @@ def do_eof(self, _: argparse.Namespace) -> Optional[bool]:
38193841 def do_quit (self , _ : argparse .Namespace ) -> Optional [bool ]:
38203842 """Exit this application"""
38213843 # Return True to stop the command loop
3844+ self .last_result = True
38223845 return True
38233846
38243847 def select (self , opts : Union [str , List [str ], List [Tuple [Any , Optional [str ]]]], prompt : str = 'Your choice? ' ) -> str :
@@ -3931,6 +3954,8 @@ def complete_set_value(
39313954 @with_argparser (set_parser , preserve_quotes = True )
39323955 def do_set (self , args : argparse .Namespace ) -> None :
39333956 """Set a settable parameter or show current settings of parameters"""
3957+ self .last_result = False
3958+
39343959 if not self .settables :
39353960 self .pwarning ("There are no settable parameters" )
39363961 return
@@ -3952,6 +3977,7 @@ def do_set(self, args: argparse.Namespace) -> None:
39523977 self .perror (f"Error setting { args .param } : { ex } " )
39533978 else :
39543979 self .poutput (f"{ args .param } - was: { orig_value !r} \n now: { new_value !r} " )
3980+ self .last_result = True
39553981 return
39563982
39573983 # Show one settable
@@ -3974,11 +4000,14 @@ def do_set(self, args: argparse.Namespace) -> None:
39744000 table = SimpleTable (cols , divider_char = self .ruler )
39754001 self .poutput (table .generate_header ())
39764002
3977- # Build the table
4003+ # Build the table and populate self.last_result
4004+ self .last_result = {} # Dict[settable_name, settable_value]
4005+
39784006 for param in sorted (to_show , key = self .default_sort_key ):
39794007 settable = self .settables [param ]
39804008 row_data = [param , settable .get_value (), settable .description ]
39814009 self .poutput (table .generate_data_row (row_data ))
4010+ self .last_result [param ] = settable .get_value ()
39824011
39834012 shell_parser = argparse_custom .DEFAULT_ARGUMENT_PARSER (description = "Execute a command as if at the OS prompt" )
39844013 shell_parser .add_argument ('command' , help = 'the command to run' , completer = shell_cmd_complete )
@@ -4182,6 +4211,7 @@ def _run_python(self, *, pyscript: Optional[str] = None) -> Optional[bool]:
41824211 after it sets up sys.argv for the script. (Defaults to None)
41834212 :return: True if running of commands should stop
41844213 """
4214+ self .last_result = False
41854215
41864216 def py_quit () -> None :
41874217 """Function callable from the interactive Python console to exit that environment"""
@@ -4198,6 +4228,8 @@ def py_quit() -> None:
41984228 self .perror ("Recursively entering interactive Python shells is not allowed" )
41994229 return None
42004230
4231+ self .last_result = True
4232+
42014233 try :
42024234 self ._in_py = True
42034235 py_code_to_run = ''
@@ -4310,6 +4342,8 @@ def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]:
43104342
43114343 :return: True if running of commands should stop
43124344 """
4345+ self .last_result = False
4346+
43134347 # Expand ~ before placing this path in sys.argv just as a shell would
43144348 args .script_path = os .path .expanduser (args .script_path )
43154349
@@ -4344,6 +4378,8 @@ def do_ipy(self, _: argparse.Namespace) -> Optional[bool]: # pragma: no cover
43444378
43454379 :return: True if running of commands should stop
43464380 """
4381+ self .last_result = False
4382+
43474383 # Detect whether IPython is installed
43484384 try :
43494385 import traitlets .config .loader as TraitletsLoader # type: ignore[import]
@@ -4368,6 +4404,8 @@ def do_ipy(self, _: argparse.Namespace) -> Optional[bool]: # pragma: no cover
43684404 self .perror ("Recursively entering interactive Python shells is not allowed" )
43694405 return None
43704406
4407+ self .last_result = True
4408+
43714409 try :
43724410 self ._in_py = True
43734411 py_bridge = PyBridge (self )
@@ -4456,6 +4494,7 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]:
44564494
44574495 :return: True if running of commands should stop
44584496 """
4497+ self .last_result = False
44594498
44604499 # -v must be used alone with no other options
44614500 if args .verbose :
@@ -4471,6 +4510,8 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]:
44714510 return None
44724511
44734512 if args .clear :
4513+ self .last_result = True
4514+
44744515 # Clear command and readline history
44754516 self .history .clear ()
44764517
@@ -4481,6 +4522,7 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]:
44814522 pass
44824523 except OSError as ex :
44834524 self .perror (f"Error removing history file '{ self .persistent_history_file } ': { ex } " )
4525+ self .last_result = False
44844526 return None
44854527
44864528 if rl_type != RlType .NONE :
@@ -4495,7 +4537,9 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]:
44954537 self .perror ("Cowardly refusing to run all previously entered commands." )
44964538 self .perror ("If this is what you want to do, specify '1:' as the range of history." )
44974539 else :
4498- return self .runcmds_plus_hooks (list (history .values ()))
4540+ stop = self .runcmds_plus_hooks (list (history .values ()))
4541+ self .last_result = True
4542+ return stop
44994543 elif args .edit :
45004544 import tempfile
45014545
@@ -4509,8 +4553,10 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]:
45094553 fobj .write (f'{ command .raw } \n ' )
45104554 try :
45114555 self .run_editor (fname )
4556+
4557+ # self.last_resort will be set by do_run_script()
45124558 # noinspection PyTypeChecker
4513- self .do_run_script (utils .quote_string (fname ))
4559+ return self .do_run_script (utils .quote_string (fname ))
45144560 finally :
45154561 os .remove (fname )
45164562 elif args .output_file :
@@ -4527,12 +4573,15 @@ def do_history(self, args: argparse.Namespace) -> Optional[bool]:
45274573 self .perror (f"Error saving history file '{ full_path } ': { ex } " )
45284574 else :
45294575 self .pfeedback (f"{ len (history )} command{ plural } saved to { full_path } " )
4576+ self .last_result = True
45304577 elif args .transcript :
4578+ # self.last_resort will be set by _generate_transcript()
45314579 self ._generate_transcript (list (history .values ()), args .transcript )
45324580 else :
45334581 # Display the history items retrieved
45344582 for idx , hi in history .items ():
45354583 self .poutput (hi .pr (idx , script = args .script , expanded = args .expanded , verbose = args .verbose ))
4584+ self .last_result = history
45364585 return None
45374586
45384587 def _get_history (self , args : argparse .Namespace ) -> 'OrderedDict[int, HistoryItem]' :
@@ -4650,6 +4699,8 @@ def _persist_history(self) -> None:
46504699
46514700 def _generate_transcript (self , history : Union [List [HistoryItem ], List [str ]], transcript_file : str ) -> None :
46524701 """Generate a transcript file from a given history of commands"""
4702+ self .last_result = False
4703+
46534704 # Validate the transcript file path to make sure directory exists and write access is available
46544705 transcript_path = os .path .abspath (os .path .expanduser (transcript_file ))
46554706 transcript_dir = os .path .dirname (transcript_path )
@@ -4732,6 +4783,7 @@ def _generate_transcript(self, history: Union[List[HistoryItem], List[str]], tra
47324783 else :
47334784 plural = 'commands and their outputs'
47344785 self .pfeedback (f"{ commands_run } { plural } saved to transcript file '{ transcript_path } '" )
4786+ self .last_result = True
47354787
47364788 edit_description = (
47374789 "Run a text editor and optionally open a file with it\n "
@@ -4749,6 +4801,8 @@ def _generate_transcript(self, history: Union[List[HistoryItem], List[str]], tra
47494801 @with_argparser (edit_parser )
47504802 def do_edit (self , args : argparse .Namespace ) -> None :
47514803 """Run a text editor and optionally open a file with it"""
4804+
4805+ # self.last_result will be set by do_shell() which is called by run_editor()
47524806 self .run_editor (args .file_path )
47534807
47544808 def run_editor (self , file_path : Optional [str ] = None ) -> None :
@@ -4802,6 +4856,7 @@ def do_run_script(self, args: argparse.Namespace) -> Optional[bool]:
48024856
48034857 :return: True if running of commands should stop
48044858 """
4859+ self .last_result = False
48054860 expanded_path = os .path .abspath (os .path .expanduser (args .script_path ))
48064861
48074862 # Add some protection against accidentally running a Python file. The happens when users
@@ -4835,9 +4890,12 @@ def do_run_script(self, args: argparse.Namespace) -> Optional[bool]:
48354890 self ._script_dir .append (os .path .dirname (expanded_path ))
48364891
48374892 if args .transcript :
4893+ # self.last_resort will be set by _generate_transcript()
48384894 self ._generate_transcript (script_commands , os .path .expanduser (args .transcript ))
48394895 else :
4840- return self .runcmds_plus_hooks (script_commands , stop_on_keyboard_interrupt = True )
4896+ stop = self .runcmds_plus_hooks (script_commands , stop_on_keyboard_interrupt = True )
4897+ self .last_result = True
4898+ return stop
48414899
48424900 finally :
48434901 with self .sigint_protection :
@@ -4871,6 +4929,7 @@ def do__relative_run_script(self, args: argparse.Namespace) -> Optional[bool]:
48714929 # NOTE: Relative path is an absolute path, it is just relative to the current script directory
48724930 relative_path = os .path .join (self ._current_script_dir or '' , file_path )
48734931
4932+ # self.last_result will be set by do_run_script()
48744933 # noinspection PyTypeChecker
48754934 return self .do_run_script (utils .quote_string (relative_path ))
48764935
0 commit comments