379 lines
12 KiB
Plaintext
379 lines
12 KiB
Plaintext
# -*- coding: utf-8 -*-
|
|
"""Startup script that adds niceties to the interactive interpreter.
|
|
|
|
This script adds the following things:
|
|
|
|
- Readline bindings, tab completion, and history (in ~/.history/python,
|
|
which can be disabled by setting NOHIST in the environment)
|
|
|
|
- Pretty printing of expression output (with Pygments highlighting)
|
|
|
|
- Pygments highlighting of tracebacks
|
|
|
|
- Function arguments in repr() for callables
|
|
|
|
- A source() function that displays the source of an arbitrary object
|
|
(in a pager, with Pygments highlighting)
|
|
|
|
Python 2.3 and newer are supported, including Python 3.x.
|
|
|
|
Note: The default versions of Python that ship with Mac OS X don't
|
|
come with readline. To get readline support, you can try a stand-alone
|
|
readline library[1], or you can use a different Python distribution
|
|
(like the one from MacPorts).
|
|
|
|
[1]: http://pypi.python.org/pypi/readline
|
|
"""
|
|
|
|
|
|
def _pythonrc_enable_readline():
|
|
"""Enable readline, tab completion, and history"""
|
|
import sys
|
|
|
|
try:
|
|
import readline
|
|
import rlcompleter
|
|
except ImportError:
|
|
sys.stderr.write('readline unavailable - tab completion disabled.\n')
|
|
return
|
|
|
|
old_complete = readline.get_completer()
|
|
|
|
def complete(text, state):
|
|
if not text:
|
|
# Insert four spaces for indentation
|
|
return (' ', None)[state]
|
|
else:
|
|
return old_complete(text, state)
|
|
readline.parse_and_bind('tab: complete')
|
|
readline.set_completer(complete)
|
|
|
|
|
|
def _pythonrc_enable_history():
|
|
import atexit
|
|
import os
|
|
try:
|
|
import readline
|
|
except:
|
|
return
|
|
|
|
# "NOHIST= python" will disable history
|
|
if 'NOHIST' not in os.environ:
|
|
history_path = os.path.expanduser('~/.history/python')
|
|
|
|
has_written = [False]
|
|
|
|
def write_history():
|
|
if not has_written[0]:
|
|
readline.write_history_file(history_path)
|
|
print('Written history to %s' % history_path)
|
|
has_written[0] = True
|
|
atexit.register(write_history)
|
|
|
|
if os.path.isfile(history_path):
|
|
try:
|
|
readline.read_history_file(history_path)
|
|
except IOError:
|
|
pass
|
|
|
|
readline.set_history_length(-1)
|
|
|
|
|
|
def _pythonrc_enable_pprint():
|
|
"""Enable pretty printing of evaluated expressions"""
|
|
import pprint
|
|
import sys
|
|
|
|
try:
|
|
if sys.platform == 'win32':
|
|
raise ImportError()
|
|
from cStringIO import StringIO
|
|
from pygments import highlight
|
|
from pygments.lexers import PythonLexer, PythonTracebackLexer
|
|
from pygments.formatters import TerminalFormatter
|
|
|
|
def pphighlight(o, *a, **kw):
|
|
s = pprint.pformat(o, *a, **kw)
|
|
try:
|
|
sys.stdout.write(
|
|
highlight(s, PythonLexer(), TerminalFormatter()))
|
|
except UnicodeError:
|
|
sys.stdout.write(s)
|
|
sys.stdout.write('\n')
|
|
|
|
_old_excepthook = sys.excepthook
|
|
|
|
def excepthook(exctype, value, traceback):
|
|
"""Prints exceptions to sys.stderr and colorizes them"""
|
|
|
|
# traceback.format_exception() isn't used because it's
|
|
# inconsistent with the built-in formatter
|
|
old_stderr = sys.stderr
|
|
sys.stderr = StringIO()
|
|
try:
|
|
_old_excepthook(exctype, value, traceback)
|
|
s = sys.stderr.getvalue()
|
|
try:
|
|
s = highlight(
|
|
s, PythonTracebackLexer(), TerminalFormatter())
|
|
except UnicodeError:
|
|
pass
|
|
old_stderr.write(s)
|
|
finally:
|
|
sys.stderr = old_stderr
|
|
|
|
sys.excepthook = excepthook
|
|
except ImportError:
|
|
pphighlight = pprint.pprint
|
|
|
|
try:
|
|
import __builtin__
|
|
except ImportError:
|
|
import builtins as __builtin__
|
|
import inspect
|
|
import pydoc
|
|
import sys
|
|
import types
|
|
|
|
help_types = [types.BuiltinFunctionType, types.BuiltinMethodType,
|
|
types.FunctionType, types.MethodType, types.ModuleType,
|
|
type,
|
|
# method_descriptor
|
|
type(list.remove)]
|
|
if hasattr(types, 'UnboundMethodType'):
|
|
help_types.append(types.UnboundMethodType)
|
|
help_types = tuple(help_types)
|
|
|
|
def _ioctl_width(fd):
|
|
|
|
from fcntl import ioctl
|
|
from struct import pack, unpack
|
|
from termios import TIOCGWINSZ
|
|
return unpack('HHHH',
|
|
ioctl(fd, TIOCGWINSZ, pack('HHHH', 0, 0, 0, 0)))[1]
|
|
|
|
def get_width():
|
|
"""Returns terminal width"""
|
|
|
|
width = 0
|
|
try:
|
|
width = _ioctl_width(0) or _ioctl_width(1) or _ioctl_width(2)
|
|
except ImportError:
|
|
pass
|
|
if not width:
|
|
import os
|
|
width = os.environ.get('COLUMNS', 0)
|
|
return width
|
|
|
|
if hasattr(inspect, 'getfullargspec'):
|
|
getargspec = inspect.getfullargspec
|
|
else:
|
|
getargspec = inspect.getargspec
|
|
|
|
def pprinthook(value):
|
|
"""Pretty print an object to sys.stdout and also save it in
|
|
__builtin__.
|
|
"""
|
|
|
|
if value is None:
|
|
return
|
|
__builtin__._ = value
|
|
|
|
if isinstance(value, help_types):
|
|
reprstr = repr(value)
|
|
try:
|
|
if inspect.isfunction(value):
|
|
parts = reprstr.split(' ')
|
|
parts[1] += inspect.formatargspec(*getargspec(value))
|
|
reprstr = ' '.join(parts)
|
|
elif inspect.ismethod(value):
|
|
parts = reprstr[:-1].split(' ')
|
|
parts[2] += inspect.formatargspec(*getargspec(value))
|
|
reprstr = ' '.join(parts) + '>'
|
|
except TypeError:
|
|
pass
|
|
sys.stdout.write(reprstr)
|
|
sys.stdout.write('\n')
|
|
if getattr(value, '__doc__', None):
|
|
sys.stdout.write('\n')
|
|
sys.stdout.write(pydoc.getdoc(value))
|
|
sys.stdout.write('\n')
|
|
else:
|
|
pphighlight(value, width=get_width() or 80)
|
|
|
|
sys.displayhook = pprinthook
|
|
|
|
|
|
def _pythonrc_fix_linecache():
|
|
"""Add source(obj) that shows the source code for a given object"""
|
|
import os
|
|
import sys
|
|
from linecache import cache
|
|
|
|
# linecache.updatecache() replacement that actually works with zips.
|
|
# See http://bugs.python.org/issue4223 for more information.
|
|
def updatecache(filename, module_globals=None):
|
|
"""Update a cache entry and return its list of lines.
|
|
If something's wrong, print a message, discard the cache entry,
|
|
and return an empty list."""
|
|
|
|
if filename in cache:
|
|
del cache[filename]
|
|
if not filename or filename[0] + filename[-1] == '<>':
|
|
return []
|
|
|
|
fullname = filename
|
|
try:
|
|
stat = os.stat(fullname)
|
|
except os.error:
|
|
basename = os.path.split(filename)[1]
|
|
|
|
if module_globals and '__loader__' in module_globals:
|
|
name = module_globals.get('__name__')
|
|
loader = module_globals['__loader__']
|
|
get_source = getattr(loader, 'get_source', None)
|
|
|
|
if name and get_source:
|
|
try:
|
|
data = get_source(name)
|
|
except (ImportError, IOError):
|
|
pass
|
|
else:
|
|
if data is None:
|
|
return []
|
|
cache[filename] = (
|
|
len(data), None,
|
|
[line + '\n' for line in data.splitlines()],
|
|
fullname
|
|
)
|
|
return cache[filename][2]
|
|
|
|
for dirname in sys.path:
|
|
try:
|
|
fullname = os.path.join(dirname, basename)
|
|
except (TypeError, AttributeError):
|
|
pass
|
|
else:
|
|
try:
|
|
stat = os.stat(fullname)
|
|
break
|
|
except os.error:
|
|
pass
|
|
else:
|
|
return []
|
|
try:
|
|
fp = open(fullname, 'rU')
|
|
lines = fp.readlines()
|
|
fp.close()
|
|
except IOError:
|
|
return []
|
|
size, mtime = stat.st_size, stat.st_mtime
|
|
cache[filename] = size, mtime, lines, fullname
|
|
return lines
|
|
|
|
import linecache
|
|
linecache.updatecache = updatecache
|
|
|
|
|
|
def source(obj):
|
|
"""Display the source code of an object.
|
|
|
|
Applies syntax highlighting if Pygments is available.
|
|
"""
|
|
import os
|
|
import sys
|
|
from inspect import findsource, getmodule, getsource, getsourcefile
|
|
from pydoc import pager
|
|
|
|
try:
|
|
# Check to see if the object is defined in a shared library, which
|
|
# findsource() doesn't do properly (see issue4050)
|
|
if not getsourcefile(obj):
|
|
raise TypeError
|
|
s = getsource(obj)
|
|
except TypeError:
|
|
sys.stderr.write("Source code unavailable (maybe it's part of "
|
|
"a C extension?)\n")
|
|
return
|
|
|
|
# Detect the module's file encoding. We could use
|
|
# tokenize.detect_encoding(), but it's only available in Python 3.
|
|
import re
|
|
enc = 'ascii'
|
|
for line in findsource(getmodule(obj))[0][:2]:
|
|
m = re.search(r'coding[:=]\s*([-\w.]+)', line)
|
|
if m:
|
|
enc = m.group(1)
|
|
if hasattr(s, 'decode'):
|
|
try:
|
|
s = s.decode(enc, 'replace')
|
|
except LookupError:
|
|
s = s.decode('ascii', 'replace')
|
|
|
|
try:
|
|
# For now, let's assume we'll never have a proper terminal on win32
|
|
if sys.platform == 'win32':
|
|
raise ImportError
|
|
from pygments import highlight
|
|
from pygments.lexers import PythonLexer
|
|
from pygments.formatters import TerminalFormatter
|
|
s = highlight(s, PythonLexer(), TerminalFormatter())
|
|
except (ImportError, UnicodeError):
|
|
pass
|
|
|
|
# Display the source code in the pager, and try to convince less not to
|
|
# escape color control codes.
|
|
has_lessopts = 'LESS' in os.environ
|
|
lessopts = os.environ.get('LESS', '')
|
|
try:
|
|
os.environ['LESS'] = lessopts + ' -R'
|
|
if hasattr(s, 'decode'):
|
|
pager(s.encode(sys.stdout.encoding, 'replace'))
|
|
else:
|
|
pager(s)
|
|
finally:
|
|
if has_lessopts:
|
|
os.environ['LESS'] = lessopts
|
|
else:
|
|
os.environ.pop('LESS', None)
|
|
|
|
if __name__ == '__main__':
|
|
__doc__ = None
|
|
|
|
# Make sure modules in the current directory can't interfere
|
|
import sys
|
|
try:
|
|
try:
|
|
cwd = sys.path.index('')
|
|
sys.path.pop(cwd)
|
|
except ValueError:
|
|
cwd = None
|
|
|
|
sys.ps1 = "\001\033[0;32m\002>>> \001\033[1;37m\002"
|
|
sys.ps2 = "\001\033[1;31m\002... \001\033[1;37m\002"
|
|
|
|
# Run installation functions and don't taint the global namespace
|
|
try:
|
|
try:
|
|
import jedi.utils
|
|
jedi.utils.setup_readline()
|
|
del jedi
|
|
except:
|
|
print('No jedi here. Coming to the dark side.')
|
|
_pythonrc_enable_readline()
|
|
del _pythonrc_enable_readline
|
|
|
|
_pythonrc_enable_history()
|
|
_pythonrc_enable_pprint()
|
|
_pythonrc_fix_linecache()
|
|
|
|
del _pythonrc_enable_history
|
|
del _pythonrc_enable_pprint
|
|
del _pythonrc_fix_linecache
|
|
finally:
|
|
if cwd is not None:
|
|
sys.path.insert(cwd, '')
|
|
del cwd
|
|
finally:
|
|
pass
|