summaryrefslogtreecommitdiff
path: root/cmd2.py
diff options
context:
space:
mode:
authorTodd Leonhardt <todd.leonhardt@gmail.com>2017-03-12 15:54:56 -0400
committerGitHub <noreply@github.com>2017-03-12 15:54:56 -0400
commite1dab4eeaa471344bf9d4f72b82d860339dfa710 (patch)
treee0fa699a0aef539784103681859f4dd78f70c1e1 /cmd2.py
parent3dbcbdc365f61cef42b740160d39d3a3c9cd0d3c (diff)
parent82180e405f758061107f5c6e5e09c3e626177b3d (diff)
downloadcmd2-git-e1dab4eeaa471344bf9d4f72b82d860339dfa710.tar.gz
Merge pull request #72 from python-cmd2/shlex
Miscellaneous code cleanup
Diffstat (limited to 'cmd2.py')
-rwxr-xr-xcmd2.py74
1 files changed, 59 insertions, 15 deletions
diff --git a/cmd2.py b/cmd2.py
index 5ad6bf40..5ef4fe8e 100755
--- a/cmd2.py
+++ b/cmd2.py
@@ -59,11 +59,13 @@ import six.moves as sm
from six.moves import zip
# Python 2 urllib2.urlopen() or Python3 urllib.request.urlopen()
+# noinspection PyUnresolvedReferences
from six.moves.urllib.request import urlopen
# Python 3 compatability hack due to no built-in file keyword in Python 3
# Due to one occurence of isinstance(<foo>, file) checking to see if something is of file type
try:
+ # noinspection PyUnboundLocalVariable,PyUnresolvedReferences
file
except NameError:
import io
@@ -72,6 +74,7 @@ except NameError:
# Detect whether IPython is installed to determine if the built-in "ipy" command should be included
ipython_available = True
try:
+ # noinspection PyUnresolvedReferences
from IPython import embed
except ImportError:
ipython_available = False
@@ -124,6 +127,16 @@ def set_use_arg_list(val):
class OptionParser(optparse.OptionParser):
+ """Subclase of optparse.OptionParser which stores a reference to the function/method it is parsing options for.
+
+ Used mostly for getting access to the do_* method's docstring when printing help.
+ """
+ def __init__(self):
+ # Call super class constructor. Need to do it in this way for Python 2 and 3 compatibility
+ optparse.OptionParser.__init__(self)
+ # The do_* method this class is parsing options for. Used for accessing docstring help.
+ self._func = None
+
def exit(self, status=0, msg=None):
self.values._exit = True
if msg:
@@ -303,6 +316,7 @@ can_clip = False
if sys.platform == "win32":
# Running on Windows
try:
+ # noinspection PyUnresolvedReferences
import win32clipboard
def get_paste_buffer():
@@ -388,6 +402,13 @@ pyparsing.ParserElement.setDefaultWhitespaceChars(' \t')
class ParsedString(str):
+ """Subclass of str which also stores a pyparsing.ParseResults object containing structured parse results."""
+ # pyarsing.ParseResults - structured parse results, to provide multiple means of access to the parsed data
+ parsed = None
+
+ # Function which did the parsing
+ parser = None
+
def full_parsed_statement(self):
new = ParsedString('%s %s' % (self.parsed.command, self.parsed.args))
new.parsed = self.parsed
@@ -514,7 +535,6 @@ class Cmd(cmd.Cmd):
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
multilineCommands = []
@@ -524,7 +544,7 @@ class Cmd(cmd.Cmd):
reserved_words = []
shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'}
terminators = [';']
- urlre = re.compile('(https?://[-\\w\\./]+)')
+ urlre = re.compile('(https?://[-\\w./]+)')
# Attributes which ARE dynamicaly settable at runtime
abbrev = True # Abbreviated commands recognized
@@ -589,11 +609,11 @@ class Cmd(cmd.Cmd):
self.initial_stdout = sys.stdout
self.history = History()
self.pystate = {}
+ # noinspection PyUnresolvedReferences
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
self._transcript_files = transcript_files
# Used to enable the ability for a Python script to quit the application
@@ -606,6 +626,13 @@ class Cmd(cmd.Cmd):
# Built-in commands don't make use of this. It is purely there for user-defined commands and convenience.
self._last_result = None
+ # Used to save state during a redirection
+ self.kept_state = None
+ self.kept_sys = None
+
+ # Used for a temp file during a pipe (needed tempfile instead of real pipe for Python 3.x prior to 3.5)
+ self._temp_filename = None
+
def poutput(self, msg):
"""Convenient shortcut for self.stdout.write(); adds newline if necessary."""
if msg:
@@ -748,18 +775,23 @@ class Cmd(cmd.Cmd):
pyparsing.Optional(fileName) + (pyparsing.stringEnd | '|')
self.inputParser.ignore(self.commentInProgress)
- def preparse(self, raw, **kwargs):
+ def preparse(self, raw):
return raw
def postparse(self, parseResult):
return parseResult
- def parsed(self, raw, **kwargs):
+ def parsed(self, raw):
+ """ This function is where the actual parsing of each line occurs.
+
+ :param raw: str - the line of text as it was entered
+ :return: ParsedString - custom subclass of str with extra attributes
+ """
if isinstance(raw, ParsedString):
p = raw
else:
# preparse is an overridable hook; default makes no changes
- s = self.preparse(raw, **kwargs)
+ s = self.preparse(raw)
s = self.inputParser.transformString(s.lstrip())
s = self.commentGrammars.transformString(s)
for (shortcut, expansion) in self.shortcuts:
@@ -777,8 +809,6 @@ class Cmd(cmd.Cmd):
p = ParsedString(result.args)
p.parsed = result
p.parser = self.parsed
- for (key, val) in kwargs.items():
- p.parsed[key] = val
return p
def postparsing_precmd(self, statement):
@@ -981,6 +1011,7 @@ class Cmd(cmd.Cmd):
# has been split out so that it can be called separately
if self.use_rawinput and self.completekey:
try:
+ # noinspection PyUnresolvedReferences
import readline
self.old_completer = readline.get_completer()
readline.set_completer(self.complete)
@@ -1000,6 +1031,7 @@ class Cmd(cmd.Cmd):
finally:
if self.use_rawinput and self.completekey:
try:
+ # noinspection PyUnresolvedReferences
import readline
readline.set_completer(self.old_completer)
except ImportError:
@@ -1514,6 +1546,7 @@ Script should contain one command per line, just like command would be typed in
return self._STOP_AND_EXIT
def cmdloop(self, intro=None):
+ callargs = None
if self.allow_cli_args:
parser = optparse.OptionParser()
parser.add_option('-t', '--test', dest='test',
@@ -1578,6 +1611,15 @@ class History(list):
return result
def search(self, target):
+ """ Search the history for a particular term.
+
+ :param target: str - term to search for
+ :return: List[str] - list of matches
+ """
+ # If there is no history yet, don't try to search through a non-existent list, just return an empty list
+ if len(self) < 1:
+ return []
+
target = target.strip()
if target[0] == target[-1] == '/' and len(target) > 1:
target = target[1:-1]
@@ -1586,7 +1628,7 @@ class History(list):
pattern = re.compile(target, re.IGNORECASE)
return [s for s in self if pattern.search(s)]
- spanpattern = re.compile(r'^\s*(?P<start>\-?\d+)?\s*(?P<separator>:|(\.{2,}))?\s*(?P<end>\-?\d+)?\s*$')
+ spanpattern = re.compile(r'^\s*(?P<start>-?\d+)?\s*(?P<separator>:|(\.{2,}))?\s*(?P<end>-?\d+)?\s*$')
def span(self, raw):
if raw.lower() in ('*', '-', 'all'):
@@ -1609,7 +1651,7 @@ class History(list):
result.reverse()
return result
- rangePattern = re.compile(r'^\s*(?P<start>[\d]+)?\s*\-\s*(?P<end>[\d]+)?\s*$')
+ rangePattern = re.compile(r'^\s*(?P<start>[\d]+)?\s*-\s*(?P<end>[\d]+)?\s*$')
def append(self, new):
new = HistoryItem(new)
@@ -1642,6 +1684,7 @@ class History(list):
end = int(end)
return self[start:end]
+ # noinspection PyUnresolvedReferences
getme = getme.strip()
if getme.startswith(r'/') and getme.endswith(r'/'):
@@ -1702,19 +1745,19 @@ class Statekeeper(object):
class Borg(object):
- '''All instances of any Borg subclass will share state.
- from Python Cookbook, 2nd Ed., recipe 6.16'''
+ """All instances of any Borg subclass will share state.
+ from Python Cookbook, 2nd Ed., recipe 6.16"""
_shared_state = {}
def __new__(cls, *a, **k):
- obj = object.__new__(cls, *a, **k)
+ obj = object.__new__(cls)
obj.__dict__ = cls._shared_state
return obj
class OutputTrap(Borg):
- '''Instantiate an OutputTrap to divert/capture ALL stdout output. For use in unit testing.
- Call `tearDown()` to return to normal output.'''
+ """Instantiate an OutputTrap to divert/capture ALL stdout output. For use in unit testing.
+ Call `tearDown()` to return to normal output."""
def __init__(self):
self.contents = ''
@@ -1855,4 +1898,5 @@ class CmdResult(namedtuple('CmdResult', ['out', 'err', 'war'])):
if __name__ == '__main__':
# If run as the main application, simply start a bare-bones cmd2 application with only built-in functionality.
app = Cmd()
+ app.debug = True
app.cmdloop()