diff options
-rwxr-xr-x | cmd2.py | 140 | ||||
-rw-r--r-- | docs/conf.py | 2 | ||||
-rw-r--r-- | docs/settingchanges.rst | 52 | ||||
-rw-r--r-- | tests/conftest.py | 4 | ||||
-rw-r--r-- | tests/test_cmd2.py | 3 |
5 files changed, 104 insertions, 97 deletions
@@ -496,45 +496,95 @@ class EmptyStatement(Exception): class Cmd(cmd.Cmd): - echo = False - case_insensitive = True # Commands recognized regardless of case - continuation_prompt = '> ' - timing = False # Prints elapsed time for each command + # TODO: Move all instance member initializations inside __init__() + + # Attributes which are NOT dynamically settable at runtime + _STOP_AND_EXIT = True # distinguish end of script file from actual exit + _STOP_SCRIPT_NO_EXIT = -999 + blankLinesAllowed = False + colorcodes = {'bold': {True: '\x1b[1m', False: '\x1b[22m'}, + 'cyan': {True: '\x1b[36m', False: '\x1b[39m'}, + 'blue': {True: '\x1b[34m', False: '\x1b[39m'}, + 'red': {True: '\x1b[31m', False: '\x1b[39m'}, + 'magenta': {True: '\x1b[35m', False: '\x1b[39m'}, + 'green': {True: '\x1b[32m', False: '\x1b[39m'}, + 'underline': {True: '\x1b[4m', False: '\x1b[24m'}, + 'yellow': {True: '\x1b[33m', False: '\x1b[39m'}, + } + commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment]) + commentGrammars.addParseAction(lambda x: '') + commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/') + current_script_dir = None + default_to_shell = False + defaultExtension = 'txt' # For ``save``, ``load``, etc. + excludeFromHistory = '''run r list l history hi ed edit li eof'''.split() + kept_state = None # make sure your terminators are not in legalChars! legalChars = u'!#$%.:?@_' + pyparsing.alphanums + pyparsing.alphas8bit - shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} - excludeFromHistory = '''run r list l history hi ed edit li eof'''.split() - default_to_shell = False + multilineCommands = [] noSpecialParse = 'set ed edit exit'.split() - defaultExtension = 'txt' # For ``save``, ``load``, etc. - default_file_name = 'command.txt' # For ``save``, ``load``, etc. - abbrev = True # Abbreviated commands recognized - current_script_dir = None + prefixParser = pyparsing.Empty() + redirector = '>' # for sending output to file reserved_words = [] - feedback_to_output = False # Do include nonessentials in >, | output - quiet = False # Do not suppress nonessential output + saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums) ^ '*')("idx") + + pyparsing.Optional(pyparsing.Word(legalChars + '/\\'))("fname") + + pyparsing.stringEnd) + shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} + terminators = [';'] + urlre = re.compile('(https?://[-\\w\\./]+)') + + # Attributes which ARE dynamicaly settable at runtime + abbrev = True # Abbreviated commands recognized + autorun_on_edit = True # Should files automatically run after editing (doesn't apply to commands) + case_insensitive = True # Commands recognized regardless of case + colors = (platform.system() != 'Windows') + continuation_prompt = '> ' debug = False + default_file_name = 'command.txt' # For ``save``, ``load``, etc. + echo = False + editor = os.environ.get('EDITOR') + if not editor: + if sys.platform[:3] == 'win': + editor = 'notepad' + else: + # Favor command-line editors first so we don't leave the terminal to edit + for editor in ['vim', 'vi', 'emacs', 'nano', 'pico', 'gedit', 'kate', 'subl', 'geany', 'atom']: + if _which(editor): + break + feedback_to_output = False # Do include nonessentials in >, | output locals_in_py = True - kept_state = None - redirector = '>' # for sending output to file - autorun_on_edit = True # Should files automatically run after editing (doesn't apply to commands) + quiet = False # Do not suppress nonessential output + timing = False # Prints elapsed time for each command + # To make an attribute settable with the "do_set" command, add it to this ... settable = stubbornDict(''' - prompt + abbrev Accept abbreviated commands + autorun_on_edit Automatically run files after editing + case_insensitive upper- and lower-case both OK colors Colorized output (*nix only) continuation_prompt On 2nd+ line of input debug Show full error stack on error default_file_name for ``save``, ``load``, etc. + echo Echo command issued into output editor Program used by ``edit`` - case_insensitive upper- and lower-case both OK feedback_to_output include nonessentials in `|`, `>` results + locals_in_py Allow access to your application in py via self + prompt The prompt issued to solicit input quiet Don't print nonessential feedback - echo Echo command issued into output timing Report execution times - abbrev Accept abbreviated commands - autorun_on_edit Automatically run files after editing ''') + def __init__(self, *args, **kwargs): + cmd.Cmd.__init__(self, *args, **kwargs) + self.initial_stdout = sys.stdout + self.history = History() + self.pystate = {} + self.shortcuts = sorted(self.shortcuts.items(), reverse=True) + self.keywords = self.reserved_words + [fname[3:] for fname in dir(self) + if fname.startswith('do_')] + self._init_parser() + self._temp_filename = None + def poutput(self, msg): '''Convenient shortcut for self.stdout.write(); adds newline if necessary.''' if msg: @@ -573,29 +623,6 @@ class Cmd(cmd.Cmd): else: print(msg) - _STOP_AND_EXIT = True # distinguish end of script file from actual exit - _STOP_SCRIPT_NO_EXIT = -999 - editor = os.environ.get('EDITOR') - if not editor: - if sys.platform[:3] == 'win': - editor = 'notepad' - else: - # Favor command-line editors first so we don't leave the terminal to edit - for editor in ['vim', 'vi', 'emacs', 'nano', 'pico', 'gedit', 'kate', 'subl', 'geany', 'atom']: - if _which(editor): - break - - colorcodes = {'bold': {True: '\x1b[1m', False: '\x1b[22m'}, - 'cyan': {True: '\x1b[36m', False: '\x1b[39m'}, - 'blue': {True: '\x1b[34m', False: '\x1b[39m'}, - 'red': {True: '\x1b[31m', False: '\x1b[39m'}, - 'magenta': {True: '\x1b[35m', False: '\x1b[39m'}, - 'green': {True: '\x1b[32m', False: '\x1b[39m'}, - 'underline': {True: '\x1b[4m', False: '\x1b[24m'}, - 'yellow': {True: '\x1b[33m', False: '\x1b[39m'}, - } - colors = (platform.system() != 'Windows') - def colorize(self, val, color): '''Given a string (``val``), returns that string wrapped in UNIX-style special characters that turn on (and then off) text color and style. @@ -631,30 +658,11 @@ class Cmd(cmd.Cmd): else: cmd.Cmd.do_help(self, arg) - def __init__(self, *args, **kwargs): - cmd.Cmd.__init__(self, *args, **kwargs) - self.initial_stdout = sys.stdout - self.history = History() - self.pystate = {} - self.shortcuts = sorted(self.shortcuts.items(), reverse=True) - self.keywords = self.reserved_words + [fname[3:] for fname in dir(self) - if fname.startswith('do_')] - self._init_parser() - self._temp_filename = None - def do_shortcuts(self, args): """Lists single-key shortcuts available.""" result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in sorted(self.shortcuts)) self.stdout.write("Single-key shortcuts for other commands:\n{}\n".format(result)) - prefixParser = pyparsing.Empty() - commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment]) - commentGrammars.addParseAction(lambda x: '') - commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/') - terminators = [';'] - blankLinesAllowed = False - multilineCommands = [] - def _init_parser(self): r''' >>> c = Cmd() @@ -1388,10 +1396,6 @@ class Cmd(cmd.Cmd): if self.autorun_on_edit or buffer: self.do_load(filename) - saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums) ^ '*')("idx") + - pyparsing.Optional(pyparsing.Word(legalChars + '/\\'))("fname") + - pyparsing.stringEnd) - def do_save(self, arg): """`save [N] [filename.ext]` @@ -1450,8 +1454,6 @@ class Cmd(cmd.Cmd): targetname = os.path.join(self.current_script_dir or '', targetname) self.do_load('%s %s' % (targetname, args)) - urlre = re.compile('(https?://[-\\w\\./]+)') - def do_load(self, arg=None): """Runs script of command(s) from a file or URL.""" # If arg is None or arg is an empty string, use the default filename diff --git a/docs/conf.py b/docs/conf.py index 59dfb8d8..fdd6db8c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -70,7 +70,7 @@ language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'pycon2010'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' diff --git a/docs/settingchanges.rst b/docs/settingchanges.rst index 290e4e08..d2bf2cc7 100644 --- a/docs/settingchanges.rst +++ b/docs/settingchanges.rst @@ -4,21 +4,21 @@ Features requiring only parameter changes Several aspects of a ``cmd2`` application's behavior can be controlled simply by setting attributes of ``App``. -A parameter can also be changed at runtime by the user *if* +A parameter can also be changed at runtime by the user *if* its name is included in the dictionary ``app.settable``. (To define your own user-settable parameters, see :ref:`parameters`) Case-insensitivity ================== -By default, all ``cmd2`` command names are case-insensitive; -``sing the blues`` and ``SiNg the blues`` are equivalent. To change this, +By default, all ``cmd2`` command names are case-insensitive; +``sing the blues`` and ``SiNg the blues`` are equivalent. To change this, set ``App.case_insensitive`` to False. Whether or not you set ``case_insensitive``, *please do not* define command method names with any uppercase letters. ``cmd2`` will probably do something evil if you do. - + Shortcuts ========= @@ -28,16 +28,16 @@ like ``!ls``. By default, the following shortcuts are defined: ``?`` help - - ``!`` + + ``!`` shell: run as OS-level command - + ``@`` load script file - + ``@@`` load script file; filename is relative to current script location - + To define more shortcuts, update the dict ``App.shortcuts`` with the {'shortcut': 'command_name'} (omit ``do_``):: @@ -56,7 +56,7 @@ shortcut:: (Cmd) !which python /usr/bin/python -However, if the parameter ``default_to_shell`` is +However, if the parameter ``default_to_shell`` is ``True``, then *every* command will be attempted on the operating system. Only if that attempt fails (i.e., produces a nonzero return value) will the @@ -90,9 +90,10 @@ Setting ``App.debug`` to ``True`` will produce detailed error stacks whenever the application generates an error. |settable| .. |settable| replace:: The user can ``set`` this parameter - during application execution. + during application execution. (See :ref:`parameters`) +.. _parameters: Other user-settable parameters ============================== @@ -101,18 +102,19 @@ A list of all user-settable parameters, with brief comments, is viewable from within a running application with:: - (Cmd) set --long - abbrev: True # Accept abbreviated commands - case_insensitive: True # upper- and lower-case both OK - colors: True # Colorized output (*nix only) - continuation_prompt: > # On 2nd+ line of input - debug: False # Show full error stack on error - default_file_name: command.txt # for ``save``, ``load``, etc. - echo: False # Echo command issued into output - editor: gedit # Program used by ``edit`` - feedback_to_output: False # include nonessentials in `|`, `>` results - prompt: (Cmd) # - quiet: False # Don't print nonessential feedback - timing: False # Report execution times - + (Cmd) set --long + abbrev: True # Accept abbreviated commands + autorun_on_edit: True # Automatically run files after editing + case_insensitive: True # upper- and lower-case both OK + colors: True # Colorized output (*nix only) + continuation_prompt: > # On 2nd+ line of input + debug: False # Show full error stack on error + default_file_name: command.txt # for ``save``, ``load``, etc. + echo: False # Echo command issued into output + editor: vim # Program used by ``edit`` + feedback_to_output: False # include nonessentials in `|`, `>` results + locals_in_py: True # Allow access to your application in py via self + prompt: (Cmd) # The prompt issued to solicit input + quiet: False # Don't print nonessential feedback + timing: False # Report execution times diff --git a/tests/conftest.py b/tests/conftest.py index 4b12f5cd..8bc11497 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,6 +55,7 @@ default_file_name: command.txt echo: False editor: vim feedback_to_output: False +locals_in_py: True prompt: (Cmd) quiet: False timing: False @@ -74,7 +75,8 @@ default_file_name: command.txt # for ``save``, ``load``, etc. echo: False # Echo command issued into output editor: vim # Program used by ``edit`` feedback_to_output: False # include nonessentials in `|`, `>` results -prompt: (Cmd) # +locals_in_py: True # Allow access to your application in py via self +prompt: (Cmd) # The prompt issued to solicit input quiet: False # Don't print nonessential feedback timing: False # Report execution times """.format(color_str) diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index b89f96d4..39669397 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -206,7 +206,8 @@ def test_base_cmdenvironment(base_app): # Settable parameters can be listed in any order, so need to validate carefully using unordered sets settable_params = {'continuation_prompt', 'default_file_name', 'prompt', 'abbrev', 'quiet', 'case_insensitive', - 'colors', 'echo', 'timing', 'editor', 'feedback_to_output', 'debug', 'autorun_on_edit'} + 'colors', 'echo', 'timing', 'editor', 'feedback_to_output', 'debug', 'autorun_on_edit', + 'locals_in_py'} out_params = set(out[2].split("Settable parameters: ")[1].split()) assert settable_params == out_params |