Mercurial > sqlpython
changeset 44:f489beb0e644
cmd2 is its own package now
author | catherine.devlin@gmail.com |
---|---|
date | Tue, 01 Apr 2008 14:58:48 -0400 |
parents | abbfb5a7e32c |
children | 71576bf684c8 |
files | cmd2.py |
diffstat | 1 files changed, 0 insertions(+), 410 deletions(-) [+] |
line wrap: on
line diff
--- a/cmd2.py Mon Feb 04 15:50:02 2008 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,410 +0,0 @@ -"""Variant on standard library's cmd with extra features. - -To use, simply override cmd2.Cmd instead of cmd.Cmd; use precisely as though you -were using the standard library's cmd, while enjoying the extra features. - -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 "!") -Settable environment parameters - -todo: -edited commands end with "EOF". Hmm. -example of flag usage -> to dump to file -""" -import cmd, re, os, sys -import flagReader - -class Cmd(cmd.Cmd): - caseInsensitive = True - multilineCommands = [] - continuationPrompt = '> ' - shortcuts = {'?': 'help', '!': 'shell', '@': 'load'} - excludeFromHistory = '''run r list l history hi ed li eof'''.split() - defaultExtension = 'txt' - defaultFileName = 'command.txt' - editor = os.environ.get('EDITOR') - if not editor: - if sys.platform[:3] == 'win': - editor = 'notepad' - settable = ['prompt', 'continuationPrompt', 'defaultFileName', 'editor', 'caseInsensitive'] - terminators = ';\n' - def do_cmdenvironment(self, args): - self.stdout.write(""" - Commands are %(casesensitive)scase-sensitive. - Commands may be terminated with: %(terminators)s - Settable parameters: %(settable)s - """ % - { 'casesensitive': 'not ' if self.caseInsensitive else '', - 'terminators': ' '.join(self.terminators), - 'settable': ' '.join(self.settable) - }) - - def __init__(self, *args, **kwargs): - cmd.Cmd.__init__(self, *args, **kwargs) - self.history = History() - - def do_shortcuts(self, args): - """Lists single-key shortcuts available.""" - result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in self.shortcuts.items()) - self.stdout.write("Single-key shortcuts for other commands:\n%s\n" % (result)) - - def onecmd(self, line): - """Interpret the argument as though it had been typed in response - to the prompt. - - This may be overridden, but should not normally need to be; - see the precmd() and postcmd() methods for useful execution hooks. - The return value is a flag indicating whether interpretation of - commands by the interpreter should stop. - - """ - try: - (command, args) = line.split(None,1) - except ValueError: - (command, args) = line, '' - if self.caseInsensitive: - command = command.lower() - statement = ' '.join([command, args]) - if command in self.multilineCommands: - statement = self.finishStatement(statement) - stop = cmd.Cmd.onecmd(self, statement) - try: - command = statement.split(None,1)[0].lower() - if command not in self.excludeFromHistory: - self.history.append(statement) - finally: - return stop - - def finishStatement(self, firstline): - statement = firstline - while not self.statementHasEnded(statement): - inp = self.pseudo_raw_input(self.continuationPrompt) - statement = '%s\n%s' % (statement, inp) - return statement - # assembling a list of lines and joining them at the end would be faster, - # but statementHasEnded needs a string arg; anyway, we're getting - # user input and users are slow. - - def pseudo_raw_input(self, prompt): - """copied from cmd's cmdloop; like raw_input, but accounts for changed stdin, stdout""" - - if self.use_rawinput: - try: - line = raw_input(prompt) - except EOFError: - line = 'EOF' - else: - self.stdout.write(prompt) - self.stdout.flush() - line = self.stdin.readline() - if not len(line): - line = 'EOF' - else: - if line[-1] == '\n': # this was always true in Cmd - line = line[:-1] - return line - - def cmdloop(self, intro=None): - """Repeatedly issue a prompt, accept input, parse an initial prefix - off the received input, and dispatch to action methods, passing them - the remainder of the line as argument. - """ - - # 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() - readline.set_completer(self.complete) - readline.parse_and_bind(self.completekey+": complete") - except ImportError: - pass - try: - if intro is not None: - self.intro = intro - if self.intro: - self.stdout.write(str(self.intro)+"\n") - stop = None - while not stop: - if self.cmdqueue: - line = self.cmdqueue.pop(0) - else: - line = self.pseudo_raw_input(self.prompt) - line = self.precmd(line) - stop = self.onecmd(line) - stop = self.postcmd(stop, line) - self.postloop() - finally: - if self.use_rawinput and self.completekey: - try: - import readline - readline.set_completer(self.old_completer) - except ImportError: - pass - - def do_EOF(self, arg): - return True - do_eof = do_EOF - - statementEndPattern = re.compile(r'[%s]\s*$' % terminators) - def statementHasEnded(self, lines): - return bool(self.statementEndPattern.search(lines)) \ - or lines[-3:] == 'EOF' - - def clean(self, s): - """cleans up a string""" - if self.caseInsensitive: - return s.strip().lower() - return s.strip() - - def parseline(self, line): - """Parse the line into a command name and a string containing - the arguments. Returns a tuple containing (command, args, line). - 'command' and 'args' may be None if the line couldn't be parsed. - """ - line = line.strip() - if not line: - return None, None, line - shortcut = self.shortcuts.get(line[0]) - if shortcut and hasattr(self, 'do_%s' % shortcut): - line = '%s %s' % (shortcut, line[1:]) - i, n = 0, len(line) - while i < n and line[i] in self.identchars: i = i+1 - cmd, arg = line[:i], line[i:].strip().strip(self.terminators) - return cmd, arg, line - - def showParam(self, param): - param = self.clean(param) - if param in self.settable: - val = getattr(self, param) - self.stdout.write('%s: %s\n' % (param, str(getattr(self, param)))) - - def do_show(self, arg): - 'Shows value of a parameter' - if arg.strip(): - self.showParam(arg) - else: - for param in self.settable: - self.showParam(param) - - def do_set(self, arg): - 'Sets a parameter' - try: - paramName, val = arg.split(None, 1) - paramName = self.clean(paramName) - if paramName not in self.settable: - raise NotSettableError - currentVal = getattr(self, paramName) - val = cast(currentVal, val.strip(self.terminators)) - setattr(self, paramName, val) - self.stdout.write('%s - was: %s\nnow: %s\n' % (paramName, currentVal, val)) - except (ValueError, AttributeError, NotSettableError), e: - self.do_show(arg) - - def do_shell(self, arg): - 'execute a command as if at the OS prompt.' - os.system(arg) - - def do_history(self, arg): - """history [arg]: lists past commands issued - - no arg -> list all - arg is integer -> list one history item, by index - arg is string -> string search - arg is /enclosed in forward-slashes/ -> regular expression search - """ - if arg: - history = self.history.get(arg) - else: - history = self.history - for hi in history: - self.stdout.write(hi.pr()) - def last_matching(self, arg): - try: - if arg: - return self.history.get(arg)[-1] - else: - return self.history[-1] - except: - return None - def do_list(self, arg): - """list [arg]: lists last command issued - - no arg -> list absolute last - arg is integer -> list one history item, by index - - arg, arg - (integer) -> list up to or after #arg - arg is string -> list last command matching string search - arg is /enclosed in forward-slashes/ -> regular expression search - """ - try: - self.stdout.write(self.last_matching(arg).pr()) - except: - pass - do_hi = do_history - do_l = do_list - do_li = do_list - - def do_ed(self, arg): - """ed: edit most recent command in text editor - ed [N]: edit numbered command from history - ed [filename]: edit specified file name - - commands are run after editor is closed.""" - if not self.editor: - print "please use 'set editor' to specify your text editing program of choice." - return - filename = self.defaultFileName - buffer = '' - try: - arg = int(arg) - buffer = self.last_matching(arg) - except: - if arg: - filename = arg - else: - buffer = self.last_matching(arg) - - if buffer: - f = open(filename, 'w') - f.write(buffer or '') - f.close() - - os.system('%s %s' % (self.editor, filename)) - self.do_load(filename) - do_edit = do_ed - - def do_save(self, fname=None): - """Saves most recent command to a file.""" - - if fname is None: - fname = self.defaultFileName - try: - f = open(fname, 'w') - f.write(self.history[-1]) - f.close() - except Exception, e: - print 'Error saving %s: %s' % (fname, str(e)) - - def do_load(self, fname=None): - """Runs command(s) from a file.""" - if fname is None: - fname = self.defaultFileName - keepstate = Statekeeper(self, ('stdin','use_rawinput','prompt','continuationPrompt')) - try: - self.stdin = open(fname, 'r') - except IOError, e: - try: - self.stdin = open('%s.%s' % (fname, self.defaultExtension), 'r') - except IOError: - print 'Problem opening file %s: \n%s' % (fname, e) - keepstate.restore() - return - self.use_rawinput = False - self.prompt = self.continuationPrompt = '' - self.cmdloop() - self.stdin.close() - keepstate.restore() - self.lastcmd = '' - - def do_run(self, arg): - """run [arg]: re-runs an earlier command - - no arg -> run most recent command - arg is integer -> run one history item, by index - arg is string -> run most recent command by string search - arg is /enclosed in forward-slashes/ -> run most recent by regex - """ - 'run [N]: runs the SQL that was run N commands ago' - runme = self.last_matching(arg) - print runme - if runme: - runme = self.precmd(runme) - stop = self.onecmd(runme) - stop = self.postcmd(stop, runme) - do_r = do_run - -class HistoryItem(str): - def __init__(self, instr): - str.__init__(self, instr) - self.lowercase = self.lower() - self.idx = None - def pr(self): - return '-------------------------[%d]\n%s\n' % (self.idx, str(self)) - -class History(list): - rangeFrom = re.compile(r'^([\d])+\s*\-$') - def append(self, new): - new = HistoryItem(new) - list.append(self, new) - new.idx = len(self) - def extend(self, new): - for n in new: - self.append(n) - def get(self, getme): - try: - getme = int(getme) - if getme < 0: - return self[:(-1 * getme)] - else: - return [self[getme-1]] - except IndexError: - return [] - except (ValueError, TypeError): - getme = getme.strip() - mtch = self.rangeFrom.search(getme) - if mtch: - return self[(int(mtch.group(1))-1):] - if getme.startswith(r'/') and getme.endswith(r'/'): - finder = re.compile(getme[1:-1], re.DOTALL | re.MULTILINE | re.IGNORECASE) - def isin(hi): - return finder.search(hi) - else: - def isin(hi): - return (getme.lower() in hi.lowercase) - return [itm for itm in self if isin(itm)] - -class NotSettableError(Exception): - pass - -def cast(current, new): - """Tries to force a new value into the same type as the current.""" - typ = type(current) - if typ == bool: - try: - return bool(int(new)) - except ValueError, TypeError: - pass - try: - new = new.lower() - except: - pass - if (new=='on') or (new[0] in ('y','t')): - return True - if (new=='off') or (new[0] in ('n','f')): - return False - else: - try: - return typ(new) - except: - pass - print "Problem setting parameter (now %s) to %s; incorrect type?" % (current, new) - return current - -class Statekeeper(object): - def __init__(self, obj, attribs): - self.obj = obj - self.attribs = attribs - self.save() - def save(self): - for attrib in self.attribs: - setattr(self, attrib, getattr(self.obj, attrib)) - def restore(self): - for attrib in self.attribs: - setattr(self.obj, attrib, getattr(self, attrib))