"""REPL enhancements for Python interactive interpreter. This module adds: - tab completion (requires readline support) - command history between sessions - syntactic sugar for 'help(stmt)' via 'stmt?' - output history To enable set the environment variables: PYTHONSTARTUP=$HOME/.startup.py Note: The PYTHONSTARTUP environment variable wont expand a '~' to the users home directory, use $HOME instead. """ try: import atexit import os import readline import rlcompleter except ImportError as err: print err else: class __ReadlineCompleter(rlcompleter.Completer): """Tab completion support for readline. If there is not text for command completion, insert 4 spaces. """ def __init__(self, tab=' '): self._complete = rlcompleter.Completer.complete self._insert_text = readline.insert_text self.tab = tab rlcompleter.Completer.__init__(self) def complete(self, text, state): if text == '': self.insert_text(self.tab) return None else: return self._complete(self, text, state) readline.parse_and_bind('tab: complete') readline.set_completer(__ReadlineCompleter().complete) class __HistoryManager(object): """Load and store command history. Commands are persisted in file '~/.history.py'. """ def __init__(self, path='~/.history.py'): self.history_path = os.path.expanduser(path) self._read_history_file = readline.read_history_file self._write_history_file = readline.write_history_file self.load_history() def load_history(self): if os.path.isfile(self.history_path): self._read_history_file(self.history_path) def save_history(self): self._write_history_file(self.history_path) atexit.register(__HistoryManager().save_history) del __ReadlineCompleter del __HistoryManager del atexit, os, readline, rlcompleter try: import inspect import string import sys except ImportError as err: pass else: class __HelpSyntax(object): """Quick access to help documentation. If statement ends with a '?', evaulate help(stmt). """ def __init__(self): self.default = sys.__excepthook__ self.isframe = inspect.isframe self.stack = inspect.stack self.stdout = sys.stdout self.whitespace = string.whitespace def __call__(self, type, value, traceback): if not isinstance(value, SyntaxError): return self.default(type, value, traceback) stmt = value.text.rstrip() if not stmt.endswith('?'): return self.default(type, value, traceback) name = stmt.rstrip('?(' + self.whitespace) self.show_help(name) def show_help(self, name): for record in self.stack(): frame = record[0] if not self.isframe(frame): continue if frame.f_globals.get('__name__') != '__main__': continue cmd = 'help({0})'.format(name) self.stdout.write(cmd) return eval(cmd, frame.f_globals, frame.f_locals) sys.excepthook = __HelpSyntax() del __HelpSyntax del inspect, string, sys try: import sys except ImportError as err: pass else: class __Prompt(object): """Maintain dictionary of output history. If '__history__' is a dict, store output results using one up number keys. Prompt is changed to identify how many keys have been populated. """ def __init__(self, prompt='>>> '): self.counter = 0 self.prompt = prompt def __radd__(self, left): return str(left) + str(self) def __str__(self): if self.is_history(): self.update_history() return 'H[{0}] {1}'.format(self.counter, self.prompt) return self.prompt def is_history(self): return isinstance(globals().get('__history__'), dict) def update_history(self): try: if (_ is not __history__ and _ is not __history__.get(self.counter)): self.counter += 1 __history__[self.counter] = _ return True except NameError: pass finally: return True # __history__ = {0: None} __history__ = None sys.ps1 = __Prompt() del __Prompt del sys