diff options
author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2017-03-12 15:54:56 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-12 15:54:56 -0400 |
commit | e1dab4eeaa471344bf9d4f72b82d860339dfa710 (patch) | |
tree | e0fa699a0aef539784103681859f4dd78f70c1e1 /cmd2.py | |
parent | 3dbcbdc365f61cef42b740160d39d3a3c9cd0d3c (diff) | |
parent | 82180e405f758061107f5c6e5e09c3e626177b3d (diff) | |
download | cmd2-git-e1dab4eeaa471344bf9d4f72b82d860339dfa710.tar.gz |
Merge pull request #72 from python-cmd2/shlex
Miscellaneous code cleanup
Diffstat (limited to 'cmd2.py')
-rwxr-xr-x | cmd2.py | 74 |
1 files changed, 59 insertions, 15 deletions
@@ -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() |