@@ -416,8 +416,8 @@ class Cmd(cmd.Cmd):
416
416
allow_cli_args = True # Should arguments passed on the command-line be processed as commands?
417
417
allow_redirection = True # Should output redirection and pipes be allowed
418
418
default_to_shell = False # Attempt to run unrecognized commands as shell commands
419
- excludeFromHistory = '''run ru r history histor histo hist his hi h edit edi ed e eof eo''' .split ()
420
- exclude_from_help = ['do_eof' ] # Commands to exclude from the help menu
419
+ excludeFromHistory = '''run ru r history histor histo hist his hi h edit edi ed e eof eo eos ''' .split ()
420
+ exclude_from_help = ['do_eof' , 'do_eos' ] # Commands to exclude from the help menu
421
421
reserved_words = []
422
422
423
423
# Attributes which ARE dynamically settable at runtime
@@ -507,8 +507,7 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, use_ipython=False
507
507
self ._temp_filename = None
508
508
509
509
# Codes used for exit conditions
510
- self ._STOP_AND_EXIT = True # distinguish end of script file from actual exit
511
- self ._STOP_SCRIPT_NO_EXIT = - 999
510
+ self ._STOP_AND_EXIT = True # cmd convention
512
511
513
512
self ._colorcodes = {'bold' : {True : '\x1b [1m' , False : '\x1b [22m' },
514
513
'cyan' : {True : '\x1b [36m' , False : '\x1b [39m' },
@@ -519,8 +518,8 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, use_ipython=False
519
518
'underline' : {True : '\x1b [4m' , False : '\x1b [24m' },
520
519
'yellow' : {True : '\x1b [33m' , False : '\x1b [39m' }}
521
520
522
- # Used by load and _relative_load commands
523
- self ._current_script_dir = None
521
+ # Used load command to store the current script dir as a LIFO queue to support _relative_load command
522
+ self ._script_dir = []
524
523
525
524
# ----- Methods related to presenting output to the user -----
526
525
@@ -829,7 +828,6 @@ def onecmd(self, line):
829
828
:return: bool - a flag indicating whether the interpretation of commands should stop
830
829
"""
831
830
statement = self .parser_manager .parsed (line )
832
- self .lastcmd = statement .parsed .raw
833
831
funcname = self ._func_named (statement .parsed .command )
834
832
if not funcname :
835
833
return self ._default (statement )
@@ -1020,9 +1018,9 @@ def do_shortcuts(self, args):
1020
1018
1021
1019
# noinspection PyUnusedLocal
1022
1020
def do_eof (self , arg ):
1023
- """Automatically called at end of loading a script or when <Ctrl>-D is pressed."""
1021
+ """Called when <Ctrl>-D is pressed."""
1024
1022
# End of script should not exit app, but <Ctrl>-D should.
1025
- return self ._STOP_SCRIPT_NO_EXIT
1023
+ return self ._STOP_AND_EXIT
1026
1024
1027
1025
def do_quit (self , arg ):
1028
1026
"""Exits this application."""
@@ -1591,6 +1589,14 @@ def do_save(self, arg):
1591
1589
except Exception as e :
1592
1590
self .perror ('Saving {!r} - {}' .format (fname , e ), traceback_war = False )
1593
1591
1592
+ @property
1593
+ def _current_script_dir (self ):
1594
+ """Accessor to get the current script directory from the _script_dir LIFO queue."""
1595
+ if self ._script_dir :
1596
+ return self ._script_dir [- 1 ]
1597
+ else :
1598
+ return None
1599
+
1594
1600
def do__relative_load (self , file_path ):
1595
1601
"""Runs commands in script file that is encoded as either ASCII or UTF-8 text.
1596
1602
@@ -1616,6 +1622,11 @@ def do__relative_load(self, file_path):
1616
1622
relative_path = os .path .join (self ._current_script_dir or '' , file_path )
1617
1623
self .do_load (relative_path )
1618
1624
1625
+ def do_eos (self , _ ):
1626
+ """Handles cleanup when a script has finished executing."""
1627
+ if self ._script_dir :
1628
+ self ._script_dir .pop ()
1629
+
1619
1630
def do_load (self , file_path ):
1620
1631
"""Runs commands in script file that is encoded as either ASCII or UTF-8 text.
1621
1632
@@ -1648,22 +1659,23 @@ def do_load(self, file_path):
1648
1659
return
1649
1660
1650
1661
try :
1651
- target = open (expanded_path )
1662
+ # Specify file encoding in Python 3, but Python 2 doesn't allow that argument to open()
1663
+ if six .PY3 :
1664
+ # Add all commands in the script to the command queue
1665
+ with open (expanded_path , encoding = 'utf-8' ) as target :
1666
+ self .cmdqueue .extend (target .read ().splitlines ())
1667
+ else :
1668
+ # Add all commands in the script to the command queue
1669
+ with open (expanded_path ) as target :
1670
+ self .cmdqueue .extend (target .read ().splitlines ())
1671
+
1672
+ # Append in an "end of script (eos)" command to cleanup the self._script_dir list
1673
+ self .cmdqueue .append ('eos' )
1652
1674
except IOError as e :
1653
1675
self .perror ('Problem accessing script from {}:\n {}' .format (expanded_path , e ))
1654
1676
return
1655
1677
1656
- keepstate = Statekeeper (self , ('stdin' , 'use_rawinput' , 'prompt' ,
1657
- 'continuation_prompt' , '_current_script_dir' ))
1658
- self .stdin = target
1659
- self .use_rawinput = False
1660
- self .prompt = self .continuation_prompt = ''
1661
- self ._current_script_dir = os .path .dirname (expanded_path )
1662
- stop = self ._cmdloop ()
1663
- self .stdin .close ()
1664
- keepstate .restore ()
1665
- self .lastcmd = ''
1666
- return stop and (stop != self ._STOP_SCRIPT_NO_EXIT )
1678
+ self ._script_dir .append (os .path .dirname (expanded_path ))
1667
1679
1668
1680
def do_run (self , arg ):
1669
1681
"""run [arg]: re-runs an earlier command
@@ -1728,16 +1740,6 @@ class TestMyAppCase(Cmd2TestCase):
1728
1740
runner = unittest .TextTestRunner ()
1729
1741
runner .run (testcase )
1730
1742
1731
- def _run_commands_at_invocation (self , callargs ):
1732
- """Runs commands provided as arguments on the command line when the application is started.
1733
-
1734
- :param callargs: List[str] - list of strings where each string is a command plus its arguments
1735
- :return: bool - True implies the entire application should exit
1736
- """
1737
- for initial_command in callargs :
1738
- if self .onecmd_plus_hooks (initial_command + '\n ' ):
1739
- return self ._STOP_AND_EXIT
1740
-
1741
1743
def cmdloop (self , intro = None ):
1742
1744
"""This is an outer wrapper around _cmdloop() which deals with extra features provided by cmd2.
1743
1745
@@ -1749,19 +1751,25 @@ def cmdloop(self, intro=None):
1749
1751
1750
1752
:param intro: str - if provided this overrides self.intro and serves as the intro banner printed once at start
1751
1753
"""
1752
- callargs = None
1753
1754
if self .allow_cli_args :
1754
1755
parser = optparse .OptionParser ()
1755
1756
parser .add_option ('-t' , '--test' , dest = 'test' ,
1756
1757
action = "store_true" ,
1757
1758
help = 'Test against transcript(s) in FILE (wildcards OK)' )
1758
1759
(callopts , callargs ) = parser .parse_args ()
1760
+
1761
+ # If transcript testing was called for, use other arguments as transcript files
1759
1762
if callopts .test :
1760
1763
self ._transcript_files = callargs
1761
1764
1765
+ # If commands were supplied at invocation, then add them to the command queue
1766
+ if callargs :
1767
+ self .cmdqueue .extend (callargs )
1768
+
1762
1769
# Always run the preloop first
1763
1770
self .preloop ()
1764
1771
1772
+ # If transcript-based regression testing was requested, then do that instead of the main loop
1765
1773
if self ._transcript_files is not None :
1766
1774
self .run_transcript_tests (self ._transcript_files )
1767
1775
else :
@@ -1773,14 +1781,8 @@ def cmdloop(self, intro=None):
1773
1781
if self .intro is not None :
1774
1782
self .stdout .write (str (self .intro ) + "\n " )
1775
1783
1776
- stop = False
1777
- # If allowed, process any commands present as arguments on the command-line, if allowed
1778
- if self .allow_cli_args :
1779
- stop = self ._run_commands_at_invocation (callargs )
1780
-
1781
- # And then call _cmdloop() if there wasn't something in those causing us to quit
1782
- if not stop :
1783
- self ._cmdloop ()
1784
+ # And then call _cmdloop() to enter the main loop
1785
+ self ._cmdloop ()
1784
1786
1785
1787
# Run the postloop() no matter what
1786
1788
self .postloop ()
0 commit comments