Created
July 25, 2011 11:25
-
-
Save aliles/1103947 to your computer and use it in GitHub Desktop.
Enhancements to Python REPL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment