6868 if rl_type == RlType .PYREADLINE :
6969
7070 # Save the original pyreadline display completion function since we need to override it and restore it
71- # noinspection PyProtectedMember
71+ # noinspection PyProtectedMember,PyUnresolvedReferences
7272 orig_pyreadline_display = readline .rl .mode ._display_completions
7373
7474 elif rl_type == RlType .GNU :
@@ -104,6 +104,7 @@ def __subclasshook__(cls, C):
104104
105105# Python 3.4 require contextlib2 for temporarily redirecting stderr and stdout
106106if sys .version_info < (3 , 5 ):
107+ # noinspection PyUnresolvedReferences
107108 from contextlib2 import redirect_stdout
108109else :
109110 from contextlib import redirect_stdout
@@ -310,52 +311,14 @@ class Cmd(cmd.Cmd):
310311
311312 Line-oriented command interpreters are often useful for test harnesses, internal tools, and rapid prototypes.
312313 """
313- # Attributes used to configure the StatementParser, best not to change these at runtime
314- multiline_commands = []
315- shortcuts = {'?' : 'help' , '!' : 'shell' , '@' : 'load' , '@@' : '_relative_load' }
316- terminators = [constants .MULTILINE_TERMINATOR ]
317-
318- # Attributes which are NOT dynamically settable at runtime
319- allow_cli_args = True # Should arguments passed on the command-line be processed as commands?
320- allow_redirection = True # Should output redirection and pipes be allowed
321- default_to_shell = False # Attempt to run unrecognized commands as shell commands
322- quit_on_sigint = False # Quit the loop on interrupt instead of just resetting prompt
323- reserved_words = []
324-
325- # Attributes which ARE dynamically settable at runtime
326- colors = constants .COLORS_TERMINAL
327- continuation_prompt = '> '
328- debug = False
329- echo = False
330- editor = os .environ .get ('EDITOR' )
331- if not editor :
332- if sys .platform [:3 ] == 'win' :
333- editor = 'notepad'
334- else :
335- # Favor command-line editors first so we don't leave the terminal to edit
336- for editor in ['vim' , 'vi' , 'emacs' , 'nano' , 'pico' , 'gedit' , 'kate' , 'subl' , 'geany' , 'atom' ]:
337- if utils .which (editor ):
338- break
339- feedback_to_output = False # Do not include nonessentials in >, | output by default (things like timing)
340- locals_in_py = False
341- quiet = False # Do not suppress nonessential output
342- timing = False # Prints elapsed time for each command
343-
344- # To make an attribute settable with the "do_set" command, add it to this ...
345- settable = {'colors' : 'Allow colorized output (valid values: Terminal, Always, Never)' ,
346- 'continuation_prompt' : 'On 2nd+ line of input' ,
347- 'debug' : 'Show full error stack on error' ,
348- 'echo' : 'Echo command issued into output' ,
349- 'editor' : 'Program used by ``edit``' ,
350- 'feedback_to_output' : 'Include nonessentials in `|`, `>` results' ,
351- 'locals_in_py' : 'Allow access to your application in py via self' ,
352- 'prompt' : 'The prompt issued to solicit input' ,
353- 'quiet' : "Don't print nonessential feedback" ,
354- 'timing' : 'Report execution times' }
314+ DEFAULT_SHORTCUTS = {'?' : 'help' , '!' : 'shell' , '@' : 'load' , '@@' : '_relative_load' }
315+ DEFAULT_EDITOR = utils .find_editor ()
355316
356317 def __init__ (self , completekey : str = 'tab' , stdin = None , stdout = None , persistent_history_file : str = '' ,
357318 persistent_history_length : int = 1000 , startup_script : Optional [str ] = None , use_ipython : bool = False ,
358- transcript_files : Optional [List [str ]] = None ) -> None :
319+ transcript_files : Optional [List [str ]] = None , allow_redirection : bool = True ,
320+ multiline_commands : Optional [List [str ]] = None , terminators : Optional [List [str ]] = None ,
321+ shortcuts : Optional [Dict [str , str ]] = None ) -> None :
359322 """An easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.
360323
361324 :param completekey: (optional) readline name of a completion key, default to Tab
@@ -366,6 +329,9 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, persistent
366329 :param startup_script: (optional) file path to a a script to load and execute at startup
367330 :param use_ipython: (optional) should the "ipy" command be included for an embedded IPython shell
368331 :param transcript_files: (optional) allows running transcript tests when allow_cli_args is False
332+ :param allow_redirection: (optional) should output redirection and pipes be allowed
333+ :param multiline_commands: (optional) list of commands allowed to accept multi-line input
334+ :param shortcuts: (optional) dictionary containing shortcuts for commands
369335 """
370336 # If use_ipython is False, make sure the do_ipy() method doesn't exit
371337 if not use_ipython :
@@ -384,31 +350,56 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, persistent
384350 # Call super class constructor
385351 super ().__init__ (completekey = completekey , stdin = stdin , stdout = stdout )
386352
353+ # Attributes which should NOT be dynamically settable at runtime
354+ self .allow_cli_args = True # Should arguments passed on the command-line be processed as commands?
355+ self .default_to_shell = False # Attempt to run unrecognized commands as shell commands
356+ self .quit_on_sigint = False # Quit the loop on interrupt instead of just resetting prompt
357+
358+ # Attributes which ARE dynamically settable at runtime
359+ self .colors = constants .COLORS_TERMINAL
360+ self .continuation_prompt = '> '
361+ self .debug = False
362+ self .echo = False
363+ self .editor = self .DEFAULT_EDITOR
364+ self .feedback_to_output = False # Do not include nonessentials in >, | output by default (things like timing)
365+ self .locals_in_py = False
366+ self .quiet = False # Do not suppress nonessential output
367+ self .timing = False # Prints elapsed time for each command
368+
369+ # To make an attribute settable with the "do_set" command, add it to this ...
370+ self .settable = {'colors' : 'Allow colorized output (valid values: Terminal, Always, Never)' ,
371+ 'continuation_prompt' : 'On 2nd+ line of input' ,
372+ 'debug' : 'Show full error stack on error' ,
373+ 'echo' : 'Echo command issued into output' ,
374+ 'editor' : 'Program used by ``edit``' ,
375+ 'feedback_to_output' : 'Include nonessentials in `|`, `>` results' ,
376+ 'locals_in_py' : 'Allow access to your application in py via self' ,
377+ 'prompt' : 'The prompt issued to solicit input' ,
378+ 'quiet' : "Don't print nonessential feedback" ,
379+ 'timing' : 'Report execution times' }
380+
387381 # Commands to exclude from the help menu and tab completion
388382 self .hidden_commands = ['eof' , 'eos' , '_relative_load' ]
389383
390384 # Commands to exclude from the history command
391385 self .exclude_from_history = '''history edit eof eos''' .split ()
392386
393387 # Command aliases and macros
394- self .aliases = dict ()
395388 self .macros = dict ()
396389
397- self ._finalize_app_parameters ()
398-
399390 self .initial_stdout = sys .stdout
400391 self .history = History ()
401392 self .pystate = {}
402393 self .py_history = []
403394 self .pyscript_name = 'app'
404- self . keywords = self . reserved_words + self . get_all_commands ()
405- self . statement_parser = StatementParser (
406- allow_redirection = self .allow_redirection ,
407- terminators = self . terminators ,
408- multiline_commands = self . multiline_commands ,
409- aliases = self . aliases ,
410- shortcuts = self . shortcuts ,
411- )
395+
396+ if shortcuts is None :
397+ shortcuts = self .DEFAULT_SHORTCUTS
398+ shortcuts = sorted ( shortcuts . items (), reverse = True )
399+ self . statement_parser = StatementParser ( allow_redirection = allow_redirection ,
400+ terminators = terminators ,
401+ multiline_commands = multiline_commands ,
402+ shortcuts = shortcuts )
412403 self ._transcript_files = transcript_files
413404
414405 # Used to enable the ability for a Python script to quit the application
@@ -568,10 +559,25 @@ def visible_prompt(self) -> str:
568559 """
569560 return utils .strip_ansi (self .prompt )
570561
571- def _finalize_app_parameters (self ) -> None :
572- """Finalize the shortcuts"""
573- # noinspection PyUnresolvedReferences
574- self .shortcuts = sorted (self .shortcuts .items (), reverse = True )
562+ @property
563+ def aliases (self ) -> Dict [str , str ]:
564+ """Read-only property to access the aliases stored in the StatementParser."""
565+ return self .statement_parser .aliases
566+
567+ @property
568+ def shortcuts (self ) -> Tuple [Tuple [str , str ]]:
569+ """Read-only property to access the shortcuts stored in the StatementParser."""
570+ return self .statement_parser .shortcuts
571+
572+ @property
573+ def allow_redirection (self ) -> bool :
574+ """Getter for the allow_redirection property that determines whether or not redirection of stdout is allowed."""
575+ return self .statement_parser .allow_redirection
576+
577+ @allow_redirection .setter
578+ def allow_redirection (self , value : bool ) -> None :
579+ """Setter for the allow_redirection property that determines whether or not redirection of stdout is allowed."""
580+ self .statement_parser .allow_redirection = value
575581
576582 def decolorized_write (self , fileobj : IO , msg : str ) -> None :
577583 """Write a string to a fileobject, stripping ANSI escape sequences if necessary
@@ -728,6 +734,7 @@ def reset_completion_defaults(self) -> None:
728734 if rl_type == RlType .GNU :
729735 readline .set_completion_display_matches_hook (self ._display_matches_gnu_readline )
730736 elif rl_type == RlType .PYREADLINE :
737+ # noinspection PyUnresolvedReferences
731738 readline .rl .mode ._display_completions = self ._display_matches_pyreadline
732739
733740 def tokens_for_completion (self , line : str , begidx : int , endidx : int ) -> Tuple [List [str ], List [str ]]:
@@ -1355,6 +1362,7 @@ def _display_matches_pyreadline(self, matches: List[str]) -> None: # pragma: no
13551362
13561363 # Print the header if one exists
13571364 if self .completion_header :
1365+ # noinspection PyUnresolvedReferences
13581366 readline .rl .mode .console .write ('\n ' + self .completion_header )
13591367
13601368 # Display matches using actual display function. This also redraws the prompt and line.
@@ -2203,6 +2211,7 @@ def _cmdloop(self) -> bool:
22032211 readline .set_completion_display_matches_hook (None )
22042212 rl_basic_quote_characters .value = old_basic_quotes
22052213 elif rl_type == RlType .PYREADLINE :
2214+ # noinspection PyUnresolvedReferences
22062215 readline .rl .mode ._display_completions = orig_pyreadline_display
22072216
22082217 self .cmdqueue .clear ()
@@ -2822,7 +2831,8 @@ def cmdenvironment(self) -> str:
28222831 Commands may be terminated with: {}
28232832 Arguments at invocation allowed: {}
28242833 Output redirection and pipes allowed: {}"""
2825- return read_only_settings .format (str (self .terminators ), self .allow_cli_args , self .allow_redirection )
2834+ return read_only_settings .format (str (self .statement_parser .terminators ), self .allow_cli_args ,
2835+ self .allow_redirection )
28262836
28272837 def show (self , args : argparse .Namespace , parameter : str = '' ) -> None :
28282838 """Shows current settings of parameters.
@@ -3047,6 +3057,7 @@ def py_quit():
30473057 # Save cmd2 history
30483058 saved_cmd2_history = []
30493059 for i in range (1 , readline .get_current_history_length () + 1 ):
3060+ # noinspection PyArgumentList
30503061 saved_cmd2_history .append (readline .get_history_item (i ))
30513062
30523063 readline .clear_history ()
@@ -3079,6 +3090,7 @@ def py_quit():
30793090 if rl_type == RlType .GNU :
30803091 readline .set_completion_display_matches_hook (None )
30813092 elif rl_type == RlType .PYREADLINE :
3093+ # noinspection PyUnresolvedReferences
30823094 readline .rl .mode ._display_completions = self ._display_matches_pyreadline
30833095
30843096 # Save off the current completer and set a new one in the Python console
@@ -3116,6 +3128,7 @@ def py_quit():
31163128 # Save py's history
31173129 self .py_history .clear ()
31183130 for i in range (1 , readline .get_current_history_length () + 1 ):
3131+ # noinspection PyArgumentList
31193132 self .py_history .append (readline .get_history_item (i ))
31203133
31213134 readline .clear_history ()
@@ -3193,10 +3206,12 @@ def do_ipy(self, _: argparse.Namespace) -> None:
31933206 exit_msg = 'Leaving IPython, back to {}' .format (sys .argv [0 ])
31943207
31953208 if self .locals_in_py :
3196- def load_ipy (self , app ):
3209+ # noinspection PyUnusedLocal
3210+ def load_ipy (cmd2_instance , app ):
31973211 embed (banner1 = banner , exit_msg = exit_msg )
31983212 load_ipy (self , bridge )
31993213 else :
3214+ # noinspection PyUnusedLocal
32003215 def load_ipy (app ):
32013216 embed (banner1 = banner , exit_msg = exit_msg )
32023217 load_ipy (bridge )
@@ -3598,6 +3613,7 @@ def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None:
35983613 if rl_type == RlType .GNU :
35993614 sys .stderr .write (terminal_str )
36003615 elif rl_type == RlType .PYREADLINE :
3616+ # noinspection PyUnresolvedReferences
36013617 readline .rl .mode .console .write (terminal_str )
36023618
36033619 # Redraw the prompt and input lines
0 commit comments