Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #1 from srathbun/develop

Refactored `_init_parser` to `input_parser` and various `Cmd` settings into `state` class.
  • Loading branch information...
commit cdd6e3ca7ac6f69eb9b955637f0a09a2ef2b2ead 2 parents 33419cc + ab74864
Tony authored
View
1  .gitignore
@@ -1,3 +1,4 @@
.DS_Store
*.pyc
*.pyo
+*.swp
View
30 cmd2.egg-info/PKG-INFO
@@ -1,37 +1,38 @@
Metadata-Version: 1.0
Name: cmd2
-Version: 0.6.4
+Version: 0.6.5
Summary: Extra features for standard library's cmd module
Home-page: http://packages.python.org/cmd2/
Author: Catherine Devlin
Author-email: catherine.devlin@gmail.com
License: MIT
-Description: Enhancements for standard library's cmd module.
+Description: Enhancements for standard library's `cmd` module.
- Drop-in replacement adds several features for command-prompt tools:
+ This drop-in replacement adds several features for command-prompt tools:
* Searchable command history (commands: "hi", "li", "run")
* Load commands from file, save to file, edit commands in file
* Multi-line commands
* Case-insensitive commands
- * Special-character shortcut commands (beyond cmd's "@" and "!")
+ * Accepts abbreviated commands when unambiguous
+ * Parse commands with flags
+ * Special-character shortcut commands beyond cmd's `@` and `!`
* Settable environment parameters
- * Parsing commands with flags
- * > (filename), >> (filename) redirect output to file
- * < (filename) gets input from file
- * bare >, >>, < redirect to/from paste buffer
- * accepts abbreviated commands when unambiguous
+ * `> _filename_`, `>> _filename_`: redirect output to _filename_
+ * `< _filename_`: get input from _filename_
+ * `>`, `>>`, `<` (without a filename): Redirect to/from the paste buffer
* `py` enters interactive Python console
- * test apps against sample session transcript (see example/example.py)
+ * Test apps against sample session transcript (see example/example.py)
- Useable without modification anywhere cmd is used; simply import cmd2.Cmd in place of cmd.Cmd.
+ Usable without modification anywhere `cmd` is used. Simply `import cmd2.Cmd` in place of `cmd.Cmd`.
- Running `2to3 <http://docs.python.org/library/2to3.html>` against ``cmd2.py``
+ Running `2to3 <http://docs.python.org/library/2to3.html>` against `cmd2.py`
generates working, Python3-based code.
- See docs at http://packages.python.org/cmd2/
+ Documentation:
+ http://packages.python.org/cmd2/
-Keywords: command prompt console cmd
+Keywords: command prompt console cmd shell cli
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
@@ -39,5 +40,6 @@ Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Software Development :: Libraries :: Python Modules
View
2  cmd2.egg-info/SOURCES.txt
@@ -1,5 +1,3 @@
-README.txt
-cmd2.py
setup.py
cmd2.egg-info/PKG-INFO
cmd2.egg-info/SOURCES.txt
View
0  cmd2/__init__.py 100644 → 100755
File mode changed
View
437 cmd2/cmd2.py
@@ -70,7 +70,7 @@
replace_with_file_contents,
write_to_paste_buffer)
-
+from .settings import (state)
# Metadata
@@ -86,28 +86,6 @@
__credits__ = '?' #@FIXME
-
-
-
-if not six.PY3:
- #
- # Packrat is causing Python3 errors that I don't understand.
- #
- # > /usr/local/Cellar/python3/3.2/lib/python3.2/site-packages/pyparsing-1.5.6-py3.2.egg/pyparsing.py(999)scanString()
- # -> nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
- # (Pdb) n
- # NameError: global name 'exc' is not defined
- #
- # (Pdb) parseFn
- # <bound method Or._parseCache of {Python style comment ^ C style comment}>
- #
- # (2011-07-28) Bug report filed:
- # https://sourceforge.net/tracker/?func=detail&atid=617311&aid=3381439&group_id=97203
- #
- pyparsing.ParserElement.enablePackrat()
-
-
-
# @FIXME
# Consider:
# * Refactoring into the parsers.py
@@ -128,416 +106,38 @@ def _attr_get_(obj, attr):
optparse.Values.get = _attr_get_ # this is the only use of _attr_get_()
-
-# @FIXME
-# Move to parsers module...without breaking
-# any code in this file
-pyparsing.ParserElement.setDefaultWhitespaceChars(' \t')
-
-
class Cmd(cmd.Cmd):
# @FIXME
# Add DocString
# (This *is* the core class, after all!)
-
-
- # @FIXME
- # CURRENT IDEA (subject to change):
- # Refactor into a Settings class, subdivided into:
- # - settable/not-settable
- # - input-related settings (parsing, case-sensitivity, shortcuts, etc.)
- # - output-related settings (printing time, prompt, etc.)
- # - component-level settings (history settings into history class, etc.)
- echo = False
- case_insensitive = True # Commands recognized regardless of case
- continuation_prompt = '> '
- timing = False # Prints elapsed time for each command
-
- # @FIXME?
- # Should this override cmd's `IDENTCHARS`?
-
- # make sure your terminators are not in legal_chars!
- legal_chars = u'!#$%.:?@_' + pyparsing.alphanums + pyparsing.alphas8bit
- shortcuts = { '?' : 'help' ,
- '!' : 'shell',
- '@' : 'load' ,
- '@@': '_relative_load'}
-
- abbrev = True # Recognize abbreviated commands
- current_script_dir = None
- debug = True
- default_file_name = 'command.txt' # For `save`, `load`, etc.
- default_to_shell = False
- default_extension = 'txt' # For `save`, `load`, etc.
- hist_exclude = {'ed','edit','eof','history','hi','l','li','list','run','r'}
- feedback_to_output = False # Do include nonessentials in >, | output
- kept_state = None
- locals_in_py = True
- no_special_parse = {'ed','edit','exit','set'}
- quiet = False # Do not suppress nonessential output
- redirector = '>' # for sending output to file
- reserved_words = []
-
- # @FIXME
- # Refactor into a Settings class (subdivided into settable/not-settable)
- settable = stubbornDict(
- '''
- abbrev Accept abbreviated commands
- 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`
- feedback_to_output include nonessentials in `|`, `>` results
- prompt Shell prompt
- quiet Don't print nonessential feedback
- timing Report execution times
- ''')
-
- # ************************************
- # End "original" variable declarations
- # ************************************
- # Starting here, variables were collected
- # together from various places within the class.
-
- _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:
- for editor in {'gedit', 'kate', 'vim', 'emacs', 'nano', 'pico'}:
- if subprocess.Popen(['which', editor], stdout=subprocess.PIPE).communicate()[0]:
- break
-
- # @FIXME
- # Refactor into [config? output?] module
- colorcodes = {
- # non-colors
- 'bold' : {True:'\x1b[1m', False:'\x1b[22m'},
- 'underline': {True:'\x1b[4m', False:'\x1b[24m'},
- # colors
- 'blue' : {True:'\x1b[34m',False:'\x1b[39m'},
- 'cyan' : {True:'\x1b[36m',False:'\x1b[39m'},
- 'green' : {True:'\x1b[32m',False:'\x1b[39m'},
- 'magenta' : {True:'\x1b[35m',False:'\x1b[39m'},
- 'red' : {True:'\x1b[31m',False:'\x1b[39m'}
- }
-
- colors = (platform.system() != 'Windows')
-
-
- # @FIXME
- # Refactor this settings block into
- # parsers.py
- allow_blank_lines = False
- comment_grammars = pyparsing.Or([ pyparsing.pythonStyleComment,
- pyparsing.cStyleComment ])
- comment_grammars.addParseAction(lambda x: '')
- comment_in_progress = '/*' + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/')
- multiline_commands = []
- prefix_parser = pyparsing.Empty()
- terminators = [';']
-
-
def __init__(self, *args, **kwargs):
# @FIXME
# Add DocString
-
# @FIXME
# Describe what happens in __init__
-
# @FIXME
# Is there a way to use `__super__`
# that is Python 2+3 compatible?
cmd.Cmd.__init__(self, *args, **kwargs)
-
+
self.initial_stdout = sys.stdout
self.history = History()
self.pystate = {}
-# self.settings_from_cmd = frozenset({
-# 'doc_header',
-# 'doc_leader',
-# 'identchars',
-# 'intro',
-# 'lastcmd',
-# 'misc_header',
-# 'nohelp',
-# 'prompt',
-# 'ruler',
-# 'undoc_header',
-# 'use_rawinput'})
-
-# self.settings_from_cmd2 = ( 'abbrev',
-# 'case_insensitive',
-# 'continuation_prompt',
-# 'current_script_dir',
-# 'debug',
-# 'default_file_name',
-# 'default_to_shell',
-# 'default_extension',
-# 'echo',
-# 'hist_exclude',
-# 'feedback_to_output',
-# 'kept_state',
-# 'legal_chars',
-# 'locals_in_py',
-# 'no_special_parse',
-# 'quiet',
-# 'redirector',
-# 'reserved_words',
-# 'shortcuts',
-# 'timing')
-
-# self.settings_for_parsing = ('abbrev',
-# 'case_insensitive',
-# 'default_to_shell',
-# 'legal_chars',
-# 'locals_in_py',
-# 'no_special_parse',
-# 'redirector',
-# 'reserved_words',
-# 'shortcuts')
-
-
+ self.defaultState = state()
+ self.currState = self.defaultState
+
# @FIXME
# Why does this need to have `reverse=True`?
- self.shortcuts = sorted(self.shortcuts.items(), reverse=True)
-
- # @FIXME
- # Refactor into parsers.py
- self._init_parser()
-
-
+ self.currState.shortcuts = sorted(self.currState.shortcuts.items(), reverse=True)
+
+
# def __getattr__(self, name):
# # Only called when attr not found
# # in the usual places
# #print("\n" + 'CALLING __getattr__({})'.format( name ) + "\n")
# #return self.settings[name]
-
-
-
- # @FIXME
- # Refactor into parsers.py
- # @FIXME
- # Refactor into NOT a god-initializer
- def _init_parser(self):
- # @FIXME
- # Add docstring
-
- # @NOTE
- # This is one of the biggest pain points of the existing code.
- # To aid in readability, I CAPITALIZED all variables that are
- # not set on `self`.
- #
- # That means that CAPITALIZED variables aren't
- # used outside of this method.
- #
- # Doing this has allowed me to more easily read what
- # variables become a part of other variables during the
- # building-up of the various parsers.
- #
- # I realize the capitalized variables is unorthodox
- # and potentially anti-convention. But after reaching out
- # to the project's creator several times over roughly 5
- # months, I'm still working on this project alone...
- # And without help, this is the only way I can move forward.
- #
- # I have a very poor understanding of the parser's
- # control flow when the user types a command and hits ENTER,
- # and until the author (or another pyparsing expert)
- # explains what's happening to me, I have to do silly
- # things like this. :-|
- #
- # Of course, if the impossible happens and this code
- # gets cleaned up, then the variables will be restored to
- # proper capitalization.
- #
- # —Zearin
- # http://github.com/zearin
- # 2012 Mar 26
-
- # ----------------------------
- # QuickRef: Pyparsing Operators
- # ----------------------------
- # ~ creates NotAny using the expression after the operator
- #
- # + creates And using the expressions before and after the operator
- #
- # | creates MatchFirst (first left-to-right match) using the
- # expressions before and after the operator
- #
- # ^ creates Or (longest match) using the expressions before and
- # after the operator
- #
- # & creates Each using the expressions before and after the operator
- #
- # * creates And by multiplying the expression by the integer operand;
- # if expression is multiplied by a 2-tuple, creates an And of
- # (min,max) expressions (similar to "{min,max}" form in
- # regular expressions); if min is None, intepret as (0,max);
- # if max is None, interpret as expr*min + ZeroOrMore(expr)
- #
- # - like + but with no backup and retry of alternatives
- #
- # * repetition of expression
- #
- # == matching expression to string; returns True if the string
- # matches the given expression
- #
- # << inserts the expression following the operator as the body of the
- # Forward expression before the operator
- # ----------------------------
-
- # Aliased for readability inside this method
- PYP = pyparsing
-
- # ----------------------------
- # Tell pyparsing how to parse
- # file input from '< filename'
- # ----------------------------
- FILENAME = PYP.Word(self.legal_chars + '/\\')
- INPUT_MARK = PYP.Literal('<')
- INPUT_MARK.setParseAction(lambda x: '')
- INPUT_FROM = FILENAME('INPUT_FROM')
- INPUT_FROM.setParseAction(replace_with_file_contents)
- # ----------------------------
-
-
- DO_NOT_PARSE = self.comment_grammars | \
- self.comment_in_progress | \
- PYP.quotedString
-
- #OUTPUT_PARSER = (PYP.Literal('>>') | (PYP.WordStart() + '>') | PYP.Regex('[^=]>'))('output')
- OUTPUT_PARSER = (PYP.Literal( 2 * self.redirector) | \
- (PYP.WordStart() + self.redirector) | \
- PYP.Regex('[^=]' + self.redirector))('output')
- PIPE = PYP.Keyword('|', identChars='|')
-
- STRING_END = PYP.stringEnd ^ '\nEOF'
-
- TERMINATOR_PARSER = PYP.Or([
- (hasattr(t, 'parseString') and t)
- or PYP.Literal(t) for t in self.terminators
- ])('terminator')
-
- # moved here from class-level variable
- self.URLRE = re.compile('(https?://[-\\w\\./]+)')
-
- self.keywords = self.reserved_words + [fname[3:] for fname in dir(self) if fname.startswith('do_')]
-
- self.comment_grammars.ignore(PYP.quotedString).setParseAction(lambda x: '')
-
- # not to be confused with `multiln_parser` (below)
- self.multiline_command = PYP.Or([
- PYP.Keyword(c, caseless=self.case_insensitive)
- for c in self.multiline_commands
- ])('multiline_command')
-
- ONELN_COMMAND = ( ~self.multiline_command +
- PYP.Word(self.legal_chars)
- )('command')
-
- #ONELN_COMMAND.setDebug(True)
-
- # CASE SENSITIVITY for
- # ONELN_COMMAND and self.multiline_command
- if self.case_insensitive:
- # Set parsers to account for case insensitivity (if appropriate)
- self.multiline_command.setParseAction(lambda x: x[0].lower())
- ONELN_COMMAND.setParseAction(lambda x: x[0].lower())
-
- self.save_parser = ( PYP.Optional(PYP.Word(PYP.nums)^'*')('idx')
- + PYP.Optional(PYP.Word(self.legal_chars + '/\\'))('fname')
- + PYP.stringEnd)
-
- AFTER_ELEMENTS = PYP.Optional(PIPE +
- PYP.SkipTo(
- OUTPUT_PARSER ^ STRING_END,
- ignore=DO_NOT_PARSE
- )('pipeTo')
- ) + \
- PYP.Optional(OUTPUT_PARSER +
- PYP.SkipTo(
- STRING_END,
- ignore=DO_NOT_PARSE
- ).setParseAction(lambda x: x[0].strip())('outputTo')
- )
-
- self.multiln_parser = (((self.multiline_command ^ ONELN_COMMAND)
- + PYP.SkipTo(
- TERMINATOR_PARSER,
- ignore=DO_NOT_PARSE
- ).setParseAction(lambda x: x[0].strip())('args')
- + TERMINATOR_PARSER)('statement')
- + PYP.SkipTo(
- OUTPUT_PARSER ^ PIPE ^ STRING_END,
- ignore=DO_NOT_PARSE
- ).setParseAction(lambda x: x[0].strip())('suffix')
- + AFTER_ELEMENTS
- )
-
- self.multiln_parser.ignore(self.comment_in_progress)
-
- self.singleln_parser = (
- ( ONELN_COMMAND + PYP.SkipTo(
- TERMINATOR_PARSER
- ^ STRING_END
- ^ PIPE
- ^ OUTPUT_PARSER,
- ignore=DO_NOT_PARSE
- ).setParseAction(lambda x:x[0].strip())('args'))('statement')
- + PYP.Optional(TERMINATOR_PARSER)
- + AFTER_ELEMENTS)
- #self.multiln_parser = self.multiln_parser('multiln_parser')
- #self.singleln_parser = self.singleln_parser('singleln_parser')
-
-
- # Configure according to `allow_blank_lines` setting
- if self.allow_blank_lines:
- self.blankln_termination_parser = PYP.NoMatch
- else:
- self.blankln_terminator = (PYP.lineEnd + PYP.lineEnd)('terminator')
- self.blankln_terminator('terminator')
- self.blankln_termination_parser = (
- (self.multiline_command ^ ONELN_COMMAND)
- + PYP.SkipTo(
- self.blankln_terminator,
- ignore=DO_NOT_PARSE
- ).setParseAction(lambda x: x[0].strip())('args')
- + self.blankln_terminator)('statement')
-
- self.blankln_termination_parser = self.blankln_termination_parser('statement')
-
- self.parser = self.prefix_parser + (STRING_END |
- self.multiln_parser |
- self.singleln_parser |
- self.blankln_termination_parser |
- self.multiline_command +
- PYP.SkipTo(
- STRING_END,
- ignore=DO_NOT_PARSE)
- )
-
- self.parser.ignore(self.comment_grammars)
-
- # a not-entirely-satisfactory way of distinguishing
- # '<' as in "import from" from
- # '<' as in "lesser than"
- self.input_parser = INPUT_MARK + \
- PYP.Optional(INPUT_FROM) + \
- PYP.Optional('>') + \
- PYP.Optional(FILENAME) + \
- (PYP.stringEnd | '|')
-
- self.input_parser.ignore(self.comment_in_progress)
-
def _cmdloop(self, intro=None):
'''
Repeatedly issue a prompt, accept input, parse an initial prefix
@@ -547,23 +147,22 @@ def _cmdloop(self, intro=None):
# An almost perfect copy from Cmd; however, the pseudo_raw_input portion
# has been split out so that it can be called separately
-
+
self.preloop()
if self.use_rawinput and self.completekey:
try:
import readline
- self.old_completer = readline.get_completer()
+ self.currState.old_completer = readline.get_completer()
readline.set_completer(self.complete)
readline.parse_and_bind(self.completekey + ': complete')
except ImportError:
- # @FIXME
- # Why is this passed?
+ # this is passed because the readline method may not be installed (I.E. windows)
pass
try:
if intro:
- self.intro = intro
+ self.currState.intro = intro
if self.intro:
- self.stdout.write(str(self.intro) + "\n")
+ self.currState.stdout.write(str(self.intro) + "\n")
stop = None
while not stop:
if self.cmdqueue:
@@ -616,7 +215,7 @@ def _func_named(self, arg):
if len(funcs) is 1:
result = 'do_' + funcs[0]
return result
-
+
def cmdloop(self):
'''
Initializes a parser and runs `_cmdloop()`.
@@ -640,7 +239,7 @@ def cmdloop(self):
if not self.run_commands_at_invocation(callargs):
self._cmdloop()
-
+
def onecmd(self, line):
'''
Interpret the argument as though it had been typed in response
@@ -663,7 +262,7 @@ def onecmd(self, line):
return self._default(statement)
stop = func(statement)
return stop
-
+
def onecmd_plus_hooks(self, line):
'''
Runs `onecmd()` with calls to hook methods at the appropriate places.
@@ -701,7 +300,7 @@ def onecmd_plus_hooks(self, line):
self.perror(str(err), statement)
finally:
return self.postparsing_postcmd(stop)
-
+
def run_commands_at_invocation(self, callargs):
'''
Runs commands in `omecmd_plus_hooks` before executing callargs.
@@ -1450,4 +1049,4 @@ def do_run(self, arg):
python myapp.py --test transcript.test
Wildcards can be used to test against multiple transcript files.
-'''
+'''
View
0  cmd2/errors.py 100644 → 100755
File mode changed
View
3  cmd2/in.vim
@@ -0,0 +1,3 @@
+set tabstop=4 "set tab character to 4 characters
+set expandtab "turn tabs into whitespace
+set shiftwidth=4 "indent width for autoindent
View
307 cmd2/input_parsers.py
@@ -0,0 +1,307 @@
+# -*- coding: UTF-8 -*-
+'''The input_parser class is the default line parser for cmd2.'''
+# __future__ first
+from __future__ import generators, \
+ print_function, \
+ with_statement
+
+
+# six: Python 2/3 Compatibility module
+# --------------------------------------------------------
+# `six` should (after `__future__`) get imported first,
+# because it deals with the different standard libraries
+# between Python 2 and 3.
+import six
+
+
+# Standard Library Imports
+# --------------------------------------------------------
+import os, \
+ platform, \
+ sys
+
+import cmd, \
+ copy, \
+ datetime, \
+ glob, \
+ optparse, \
+ re, \
+ subprocess, \
+ tempfile, \
+ traceback
+
+# From: http://packages.python.org/six/#module-six.moves
+#
+# "NOTE: The urllib, urllib2, and urlparse modules have been combined
+# in the urllib package in Python 3.
+#
+# six.moves doesn’t not support their renaming because their
+# members have been mixed across several modules in that package."
+import urllib
+
+from optparse import make_option
+from code import (InteractiveConsole ,
+ InteractiveInterpreter)
+
+
+# Third Party Imports
+# --------------------------------------------------------
+import pyparsing
+
+
+# Cmd2 Modules
+# --------------------------------------------------------
+from .errors import (EmbeddedConsoleExit ,
+ EmptyStatement ,
+ NotSettableError ,
+ PasteBufferError ,
+ PASTEBUFF_ERR)
+
+from .parsers import (ParsedString,
+ options ,
+ options_defined)
+
+from .support import (History,
+ Statekeeper,
+ stubbornDict,
+ cast,
+ can_clip,
+ get_paste_buffer,
+ replace_with_file_contents,
+ write_to_paste_buffer)
+
+if not six.PY3:
+ #
+ # Packrat is causing Python3 errors that I don't understand.
+ #
+ # > /usr/local/Cellar/python3/3.2/lib/python3.2/site-packages/pyparsing-1.5.6-py3.2.egg/pyparsing.py(999)scanString()
+ # -> nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
+ # (Pdb) n
+ # NameError: global name 'exc' is not defined
+ #
+ # (Pdb) parseFn
+ # <bound method Or._parseCache of {Python style comment ^ C style comment}>
+ #
+ # (2011-07-28) Bug report filed:
+ # https://sourceforge.net/tracker/?func=detail&atid=617311&aid=3381439&group_id=97203
+ #
+ pyparsing.ParserElement.enablePackrat()
+
+# @FIXME
+# Move to parsers module...without breaking
+# any code in this file
+pyparsing.ParserElement.setDefaultWhitespaceChars(' \t')
+
+class input_parser(object):
+ '''This class accepts an input string, and returns a parsed representation.'''
+
+ def __init__(self, currState):
+ # @FIXME
+ # Add docstring
+
+ # @NOTE
+ # This is one of the biggest pain points of the existing code.
+ # To aid in readability, I CAPITALIZED all variables that are
+ # not set on `self`.
+ #
+ # That means that CAPITALIZED variables aren't
+ # used outside of this method.
+ #
+ # Doing this has allowed me to more easily read what
+ # variables become a part of other variables during the
+ # building-up of the various parsers.
+ #
+ # I realize the capitalized variables is unorthodox
+ # and potentially anti-convention. But after reaching out
+ # to the project's creator several times over roughly 5
+ # months, I'm still working on this project alone...
+ # And without help, this is the only way I can move forward.
+ #
+ # I have a very poor understanding of the parser's
+ # control flow when the user types a command and hits ENTER,
+ # and until the author (or another pyparsing expert)
+ # explains what's happening to me, I have to do silly
+ # things like this. :-|
+ #
+ # Of course, if the impossible happens and this code
+ # gets cleaned up, then the variables will be restored to
+ # proper capitalization.
+ #
+ # —Zearin
+ # http://github.com/zearin
+ # 2012 Mar 26
+
+ # ----------------------------
+ # QuickRef: Pyparsing Operators
+ # ----------------------------
+ # ~ creates NotAny using the expression after the operator
+ #
+ # + creates And using the expressions before and after the operator
+ #
+ # | creates MatchFirst (first left-to-right match) using the
+ # expressions before and after the operator
+ #
+ # ^ creates Or (longest match) using the expressions before and
+ # after the operator
+ #
+ # & creates Each using the expressions before and after the operator
+ #
+ # * creates And by multiplying the expression by the integer operand;
+ # if expression is multiplied by a 2-tuple, creates an And of
+ # (min,max) expressions (similar to "{min,max}" form in
+ # regular expressions); if min is None, intepret as (0,max);
+ # if max is None, interpret as expr*min + ZeroOrMore(expr)
+ #
+ # - like + but with no backup and retry of alternatives
+ #
+ # * repetition of expression
+ #
+ # == matching expression to string; returns True if the string
+ # matches the given expression
+ #
+ # << inserts the expression following the operator as the body of the
+ # Forward expression before the operator
+ # ----------------------------
+
+ # Aliased for readability inside this method
+ PYP = pyparsing
+
+ # ----------------------------
+ # Tell pyparsing how to parse
+ # file input from '< filename'
+ # ----------------------------
+ FILENAME = PYP.Word(currState.legal_chars + '/\\')
+ INPUT_MARK = PYP.Literal('<')
+ INPUT_MARK.setParseAction(lambda x: '')
+ INPUT_FROM = FILENAME('INPUT_FROM')
+ INPUT_FROM.setParseAction(replace_with_file_contents)
+ # ----------------------------
+
+
+ DO_NOT_PARSE = currState.comment_grammars | \
+ currState.comment_in_progress | \
+ PYP.quotedString
+
+ #OUTPUT_PARSER = (PYP.Literal('>>') | (PYP.WordStart() + '>') | PYP.Regex('[^=]>'))('output')
+ OUTPUT_PARSER = (PYP.Literal( 2 * currState.redirector) | \
+ (PYP.WordStart() + currState.redirector) | \
+ PYP.Regex('[^=]' + currState.redirector))('output')
+
+ PIPE = PYP.Keyword('|', identChars='|')
+
+ STRING_END = PYP.stringEnd ^ '\nEOF'
+
+ TERMINATOR_PARSER = PYP.Or([
+ (hasattr(t, 'parseString') and t)
+ or PYP.Literal(t) for t in currState.terminators
+ ])('terminator')
+
+ # moved here from class-level variable
+ currState.URLRE = re.compile('(https?://[-\\w\\./]+)')
+
+ currState.keywords = currState.reserved_words + [fname[3:] for fname in dir(self) if fname.startswith('do_')]
+
+ currState.comment_grammars.ignore(PYP.quotedString).setParseAction(lambda x: '')
+
+ # not to be confused with `multiln_parser` (below)
+ currState.multiline_command = PYP.Or([
+ PYP.Keyword(c, caseless=currState.case_insensitive)
+ for c in currState.multiline_commands
+ ])('multiline_command')
+
+ ONELN_COMMAND = ( ~currState.multiline_command +
+ PYP.Word(currState.legal_chars)
+ )('command')
+
+ #ONELN_COMMAND.setDebug(True)
+
+ # CASE SENSITIVITY for
+ # ONELN_COMMAND and self.multiline_command
+ if currState.case_insensitive:
+ # Set parsers to account for case insensitivity (if appropriate)
+ currState.multiline_command.setParseAction(lambda x: x[0].lower())
+ ONELN_COMMAND.setParseAction(lambda x: x[0].lower())
+
+ currState.save_parser = ( PYP.Optional(PYP.Word(PYP.nums)^'*')('idx')
+ + PYP.Optional(PYP.Word(currState.legal_chars + '/\\'))('fname')
+ + PYP.stringEnd)
+
+ AFTER_ELEMENTS = PYP.Optional(PIPE +
+ PYP.SkipTo(
+ OUTPUT_PARSER ^ STRING_END,
+ ignore=DO_NOT_PARSE
+ )('pipeTo')
+ ) + \
+ PYP.Optional(OUTPUT_PARSER +
+ PYP.SkipTo(
+ STRING_END,
+ ignore=DO_NOT_PARSE
+ ).setParseAction(lambda x: x[0].strip())('outputTo')
+ )
+
+ currState.multiln_parser = (((currState.multiline_command ^ ONELN_COMMAND)
+ + PYP.SkipTo(
+ TERMINATOR_PARSER,
+ ignore=DO_NOT_PARSE
+ ).setParseAction(lambda x: x[0].strip())('args')
+ + TERMINATOR_PARSER)('statement')
+ + PYP.SkipTo(
+ OUTPUT_PARSER ^ PIPE ^ STRING_END,
+ ignore=DO_NOT_PARSE
+ ).setParseAction(lambda x: x[0].strip())('suffix')
+ + AFTER_ELEMENTS
+ )
+
+ currState.multiln_parser.ignore(currState.comment_in_progress)
+
+ currState.singleln_parser = (
+ ( ONELN_COMMAND + PYP.SkipTo(
+ TERMINATOR_PARSER
+ ^ STRING_END
+ ^ PIPE
+ ^ OUTPUT_PARSER,
+ ignore=DO_NOT_PARSE
+ ).setParseAction(lambda x:x[0].strip())('args'))('statement')
+ + PYP.Optional(TERMINATOR_PARSER)
+ + AFTER_ELEMENTS)
+ #self.multiln_parser = self.multiln_parser('multiln_parser')
+ #self.singleln_parser = self.singleln_parser('singleln_parser')
+
+
+ # Configure according to `allow_blank_lines` setting
+ if currState.allow_blank_lines:
+ currState.blankln_termination_parser = PYP.NoMatch
+ else:
+ currState.blankln_terminator = (PYP.lineEnd + PYP.lineEnd)('terminator')
+ currState.blankln_terminator('terminator')
+ currState.blankln_termination_parser = (
+ (currState.multiline_command ^ ONELN_COMMAND)
+ + PYP.SkipTo(
+ currState.blankln_terminator,
+ ignore=DO_NOT_PARSE
+ ).setParseAction(lambda x: x[0].strip())('args')
+ + currState.blankln_terminator)('statement')
+
+ currState.blankln_termination_parser = currState.blankln_termination_parser('statement')
+
+ currState.parser = currState.prefix_parser + (STRING_END |
+ currState.multiln_parser |
+ currState.singleln_parser |
+ currState.blankln_termination_parser |
+ currState.multiline_command +
+ PYP.SkipTo(
+ STRING_END,
+ ignore=DO_NOT_PARSE)
+ )
+
+ currState.parser.ignore(currState.comment_grammars)
+ # a not-entirely-satisfactory way of distinguishing
+ # '<' as in "import from" from
+ # '<' as in "lesser than"
+ currState.input_parser = INPUT_MARK + \
+ PYP.Optional(INPUT_FROM) + \
+ PYP.Optional('>') + \
+ PYP.Optional(FILENAME) + \
+ (PYP.stringEnd | '|')
+
+ currState.input_parser.ignore(currState.comment_in_progress)
View
0  cmd2/legacy_tests.py 100644 → 100755
File mode changed
View
3  cmd2/out.vim
@@ -0,0 +1,3 @@
+set tabstop=4 "set tab character to 4 characters
+set noexpandtab "turn off tabs into whitespace
+set shiftwidth=4 "indent width for autoindent
View
6 cmd2/parsers.py 100644 → 100755
@@ -24,7 +24,8 @@
# Third Party Imports
# --------------------------------------------------------
-import argh, \ # includes argparse
+# argh includes argparse
+import argh, \
pyparsing
__all__ = [ 'OptionParser',
@@ -75,7 +76,6 @@ def print_help(self, *args, **kwargs):
class ParsedString(str):
# @FIXME
# Add DocString
-
def full_parsed_statement(self):
# @FIXME
# Add DocString
@@ -183,4 +183,4 @@ def new_func(instance, arg):
new_func.__doc__ = '%s\n%s' % (func.__doc__, opt_parser.format_help())
return new_func
- return option_setup
+ return option_setup
View
225 cmd2/settings.py
@@ -0,0 +1,225 @@
+# -*- coding: UTF-8 -*-
+'''This file contains the state class and related code. It holds all of the settings for the current
+ state of the cmd2 instance.'''
+# __future__ first
+from __future__ import generators, \
+ print_function, \
+ with_statement
+
+
+# six: Python 2/3 Compatibility module
+# --------------------------------------------------------
+# `six` should (after `__future__`) get imported first,
+# because it deals with the different standard libraries
+# between Python 2 and 3.
+import six
+
+
+# Standard Library Imports
+# --------------------------------------------------------
+import os, \
+ platform, \
+ sys
+
+import cmd, \
+ copy, \
+ datetime, \
+ glob, \
+ optparse, \
+ re, \
+ subprocess, \
+ tempfile, \
+ traceback
+
+# From: http://packages.python.org/six/#module-six.moves
+#
+# "NOTE: The urllib, urllib2, and urlparse modules have been combined
+# in the urllib package in Python 3.
+#
+# six.moves doesn’t not support their renaming because their
+# members have been mixed across several modules in that package."
+import urllib
+
+from optparse import make_option
+from code import (InteractiveConsole ,
+ InteractiveInterpreter)
+
+
+# Third Party Imports
+# --------------------------------------------------------
+import pyparsing
+
+
+# Cmd2 Modules
+# --------------------------------------------------------
+from .errors import (EmbeddedConsoleExit ,
+ EmptyStatement ,
+ NotSettableError ,
+ PasteBufferError ,
+ PASTEBUFF_ERR)
+
+from .parsers import (ParsedString,
+ options ,
+ options_defined)
+
+from .support import (History,
+ Statekeeper,
+ stubbornDict,
+ cast,
+ can_clip,
+ get_paste_buffer,
+ replace_with_file_contents,
+ write_to_paste_buffer)
+
+from .input_parsers import (input_parser)
+
+class state(object):
+ '''The state class contains an intialized group of settings for a cmd2 instance.'''
+ # CURRENT IDEA (subject to change):
+ # Refactor into a Settings class, subdivided into:
+ # - settable/not-settable
+ # - input-related settings (parsing, case-sensitivity, shortcuts, etc.)
+ # - output-related settings (printing time, prompt, etc.)
+ # - component-level settings (history settings into history class, etc.)
+ def __init__(self):
+ self.echo = False
+ self.case_insensitive = True # Commands recognized regardless of case
+ self.continuation_prompt = '> '
+ self.timing = False # Prints elapsed time for each command
+
+ # @FIXME?
+ # Should this override cmd's `IDENTCHARS`?
+
+ # make sure your terminators are not in legal_chars!
+ self.legal_chars = u'!#$%.:?@_' + pyparsing.alphanums + pyparsing.alphas8bit
+ self.shortcuts = { '?' : 'help' ,
+ '!' : 'shell',
+ '@' : 'load' ,
+ '@@': '_relative_load'}
+
+ self.abbrev = True # Recognize abbreviated commands
+ self.current_script_dir = None
+ self.debug = True
+ self.default_file_name = 'command.txt' # For `save`, `load`, etc.
+ self.default_to_shell = False
+ self.default_extension = 'txt' # For `save`, `load`, etc.
+ self.hist_exclude = {'ed','edit','eof','history','hi','l','li','list','run','r'}
+ self.feedback_to_output = False # Do include nonessentials in >, | output
+ self.kept_state = None
+ self.locals_in_py = True
+ self.no_special_parse = {'ed','edit','exit','set'}
+ self.quiet = False # Do not suppress nonessential output
+ self.redirector = '>' # for sending output to file
+ self.reserved_words = []
+
+ # @FIXME
+ # Refactor into a Settings class (subdivided into settable/not-settable)
+ self.settable = stubbornDict(
+ '''
+ abbrev Accept abbreviated commands
+ 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`
+ feedback_to_output include nonessentials in `|`, `>` results
+ prompt Shell prompt
+ quiet Don't print nonessential feedback
+ timing Report execution times
+ ''')
+
+ # ************************************
+ # End "original" variable declarations
+ # ************************************
+ # Starting here, variables were collected
+ # together from various places within the class.
+
+ self._STOP_AND_EXIT = True # distinguish end of script file from actual exit
+ self._STOP_SCRIPT_NO_EXIT = -999
+
+ self.editor = os.environ.get('EDITOR')
+ if not self.editor:
+ if sys.platform[:3] == 'win':
+ self.editor = 'notepad'
+ else:
+ for self.editor in {'gedit', 'kate', 'vim', 'emacs', 'nano', 'pico'}:
+ if subprocess.Popen(['which', self.editor], stdout=subprocess.PIPE).communicate()[0]:
+ break
+
+ # @FIXME
+ # Refactor into [config? output?] module
+ self.colorcodes = {
+ # non-colors
+ 'bold' : {True:'\x1b[1m', False:'\x1b[22m'},
+ 'underline': {True:'\x1b[4m', False:'\x1b[24m'},
+ # colors
+ 'blue' : {True:'\x1b[34m',False:'\x1b[39m'},
+ 'cyan' : {True:'\x1b[36m',False:'\x1b[39m'},
+ 'green' : {True:'\x1b[32m',False:'\x1b[39m'},
+ 'magenta' : {True:'\x1b[35m',False:'\x1b[39m'},
+ 'red' : {True:'\x1b[31m',False:'\x1b[39m'}
+ }
+
+ self.colors = (platform.system() != 'Windows')
+
+ # @FIXME
+ # Refactor this settings block into
+ # parsers.py
+ self.allow_blank_lines = False
+ self.comment_grammars = pyparsing.Or([ pyparsing.pythonStyleComment,
+ pyparsing.cStyleComment ])
+ self.comment_grammars.addParseAction(lambda x: '')
+ self.comment_in_progress = '/*' + pyparsing.SkipTo(pyparsing.stringEnd ^ '*/')
+ self.multiline_commands = []
+ self.prefix_parser = pyparsing.Empty()
+ self.terminators = [';']
+
+ self.input_parser = input_parser(self)
+
+# @FIXME
+# commented out in Cmd, Purpose?
+# self.settings_from_cmd = frozenset({
+# 'doc_header',
+# 'doc_leader',
+# 'identchars',
+# 'intro',
+# 'lastcmd',
+# 'misc_header',
+# 'nohelp',
+# 'prompt',
+# 'ruler',
+# 'undoc_header',
+# 'use_rawinput'})
+
+# self.settings_from_cmd2 = ( 'abbrev',
+# 'case_insensitive',
+# 'continuation_prompt',
+# 'current_script_dir',
+# 'debug',
+# 'default_file_name',
+# 'default_to_shell',
+# 'default_extension',
+# 'echo',
+# 'hist_exclude',
+# 'feedback_to_output',
+# 'kept_state',
+# 'legal_chars',
+# 'locals_in_py',
+# 'no_special_parse',
+# 'quiet',
+# 'redirector',
+# 'reserved_words',
+# 'shortcuts',
+# 'timing')
+
+# self.settings_for_parsing = ('abbrev',
+# 'case_insensitive',
+# 'default_to_shell',
+# 'legal_chars',
+# 'locals_in_py',
+# 'no_special_parse',
+# 'redirector',
+# 'reserved_words',
+# 'shortcuts')
View
0  cmd2/support.py 100644 → 100755
File mode changed
View
50 tests/doctests/doctest_Cmd._init_parser.txt
@@ -4,25 +4,25 @@
>>> c.multilineCommands = ['multiline']
>>> c.case_insensitive = True
>>> c._init_parser()
->>> print (c.parser.parseString('').dump())
+>>> print (c.currState.parser.parseString('').dump())
[]
->>> print (c.parser.parseString('').dump())
+>>> print (c.currState.parser.parseString('').dump())
[]
->>> print (c.parser.parseString('/* empty command */').dump())
+>>> print (c.currState.parser.parseString('/* empty command */').dump())
[]
->>> print (c.parser.parseString('plainword').dump())
+>>> print (c.currState.parser.parseString('plainword').dump())
['plainword', '']
- command: plainword
- statement: ['plainword', '']
- command: plainword
->>> print (c.parser.parseString('termbare;').dump())
+>>> print (c.currState.parser.parseString('termbare;').dump())
['termbare', '', ';', '']
- command: termbare
- statement: ['termbare', '', ';']
- command: termbare
- terminator: ;
- terminator: ;
->>> print (c.parser.parseString('termbare; suffx').dump())
+>>> print (c.currState.parser.parseString('termbare; suffx').dump())
['termbare', '', ';', 'suffx']
- command: termbare
- statement: ['termbare', '', ';']
@@ -30,19 +30,19 @@
- terminator: ;
- suffix: suffx
- terminator: ;
->>> print (c.parser.parseString('barecommand').dump())
+>>> print (c.currState.parser.parseString('barecommand').dump())
['barecommand', '']
- command: barecommand
- statement: ['barecommand', '']
- command: barecommand
->>> print (c.parser.parseString('COMmand with args').dump())
+>>> print (c.currState.parser.parseString('COMmand with args').dump())
['command', 'with args']
- args: with args
- command: command
- statement: ['command', 'with args']
- args: with args
- command: command
->>> print (c.parser.parseString('command with args and terminator; and suffix').dump())
+>>> print (c.currState.parser.parseString('command with args and terminator; and suffix').dump())
['command', 'with args and terminator', ';', 'and suffix']
- args: with args and terminator
- command: command
@@ -52,20 +52,20 @@
- terminator: ;
- suffix: and suffix
- terminator: ;
->>> print (c.parser.parseString('simple | piped').dump())
+>>> print (c.currState.parser.parseString('simple | piped').dump())
['simple', '', '|', ' piped']
- command: simple
- pipeTo: piped
- statement: ['simple', '']
- command: simple
->>> print (c.parser.parseString('double-pipe || is not a pipe').dump())
+>>> print (c.currState.parser.parseString('double-pipe || is not a pipe').dump())
['double', '-pipe || is not a pipe']
- args: -pipe || is not a pipe
- command: double
- statement: ['double', '-pipe || is not a pipe']
- args: -pipe || is not a pipe
- command: double
->>> print (c.parser.parseString('command with args, terminator;sufx | piped').dump())
+>>> print (c.currState.parser.parseString('command with args, terminator;sufx | piped').dump())
['command', 'with args, terminator', ';', 'sufx', '|', ' piped']
- args: with args, terminator
- command: command
@@ -76,7 +76,7 @@
- terminator: ;
- suffix: sufx
- terminator: ;
->>> print (c.parser.parseString('output into > afile.txt').dump())
+>>> print (c.currState.parser.parseString('output into > afile.txt').dump())
['output', 'into', '>', 'afile.txt']
- args: into
- command: output
@@ -85,7 +85,7 @@
- statement: ['output', 'into']
- args: into
- command: output
->>> print (c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump())
+>>> print (c.currState.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump())
['output', 'into', ';', 'sufx', '|', ' pipethrume plz', '>', 'afile.txt']
- args: into
- command: output
@@ -98,7 +98,7 @@
- terminator: ;
- suffix: sufx
- terminator: ;
->>> print (c.parser.parseString('output to paste buffer >> ').dump())
+>>> print (c.currState.parser.parseString('output to paste buffer >> ').dump())
['output', 'to paste buffer', '>>', '']
- args: to paste buffer
- command: output
@@ -106,7 +106,7 @@
- statement: ['output', 'to paste buffer']
- args: to paste buffer
- command: output
->>> print (c.parser.parseString('ignore the /* commented | > */ stuff;').dump())
+>>> print (c.currState.parser.parseString('ignore the /* commented | > */ stuff;').dump())
['ignore', 'the /* commented | > */ stuff', ';', '']
- args: the /* commented | > */ stuff
- command: ignore
@@ -115,7 +115,7 @@
- command: ignore
- terminator: ;
- terminator: ;
->>> print (c.parser.parseString('has > inside;').dump())
+>>> print (c.currState.parser.parseString('has > inside;').dump())
['has', '> inside', ';', '']
- args: > inside
- command: has
@@ -124,10 +124,10 @@
- command: has
- terminator: ;
- terminator: ;
->>> print (c.parser.parseString('multiline has > inside an unfinished command').dump())
+>>> print (c.currState.parser.parseString('multiline has > inside an unfinished command').dump())
['multiline', ' has > inside an unfinished command']
- multilineCommand: multiline
->>> print (c.parser.parseString('multiline has > inside;').dump())
+>>> print (c.currState.parser.parseString('multiline has > inside;').dump())
['multiline', 'has > inside', ';', '']
- args: has > inside
- multilineCommand: multiline
@@ -136,10 +136,10 @@
- multilineCommand: multiline
- terminator: ;
- terminator: ;
->>> print (c.parser.parseString(r'multiline command /* with comment in progress;').dump())
+>>> print (c.currState.parser.parseString(r'multiline command /* with comment in progress;').dump())
['multiline', ' command /* with comment in progress;']
- multilineCommand: multiline
->>> print (c.parser.parseString('multiline command /* with comment complete */ is done;').dump())
+>>> print (c.currState.parser.parseString('multiline command /* with comment complete */ is done;').dump())
['multiline', 'command /* with comment complete */ is done', ';', '']
- args: command /* with comment complete */ is done
- multilineCommand: multiline
@@ -148,7 +148,7 @@
- multilineCommand: multiline
- terminator: ;
- terminator: ;
->>> print (c.parser.parseString('multiline command ends\n\n').dump())
+>>> print (c.currState.parser.parseString('multiline command ends\n\n').dump())
['multiline', 'command ends', '\n', '\n']
- args: command ends
- multilineCommand: multiline
@@ -157,7 +157,7 @@
- multilineCommand: multiline
- terminator: ['\n', '\n']
- terminator: ['\n', '\n']
->>> print (c.parser.parseString('multiline command "with term; ends" now\n\n').dump())
+>>> print (c.currState.parser.parseString('multiline command "with term; ends" now\n\n').dump())
['multiline', 'command "with term; ends" now', '\n', '\n']
- args: command "with term; ends" now
- multilineCommand: multiline
@@ -166,10 +166,10 @@
- multilineCommand: multiline
- terminator: ['\n', '\n']
- terminator: ['\n', '\n']
->>> print (c.parser.parseString('what if "quoted strings /* seem to " start comments?').dump())
+>>> print (c.currState.parser.parseString('what if "quoted strings /* seem to " start comments?').dump())
['what', 'if "quoted strings /* seem to " start comments?']
- args: if "quoted strings /* seem to " start comments?
- command: what
- statement: ['what', 'if "quoted strings /* seem to " start comments?']
- args: if "quoted strings /* seem to " start comments?
- - command: what
+ - command: what
Please sign in to comment.
Something went wrong with that request. Please try again.