/
repl.py
139 lines (110 loc) · 4.91 KB
/
repl.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""
Utility for creating a Python repl.
::
from prompt_toolkit.contrib.repl import embed
embed(globals(), locals(), vi_mode=False)
"""
# Warning: don't import `print_function` from __future__, otherwise we will
# also get the print_function inside `eval` on Python 2.7.
from __future__ import unicode_literals
from pygments import highlight
from pygments.formatters.terminal256 import Terminal256Formatter
from pygments.lexers import PythonTracebackLexer
from prompt_toolkit import AbortAction, Exit
from prompt_toolkit.contrib.python_input import PythonCommandLineInterface, PythonStyle, AutoCompletionStyle
from six import exec_
import sys
import os
import traceback
__all__ = ('PythonRepl', 'embed')
class PythonRepl(PythonCommandLineInterface):
def start_repl(self, startup_paths=None):
"""
Start the Read-Eval-Print Loop.
:param startup_paths: Array of paths to Python files.
"""
self._execute_startup(startup_paths)
# Run REPL loop until Exit.
try:
while True:
# Read
document = self.read_input(
on_abort=AbortAction.RETRY,
on_exit=AbortAction.RAISE_EXCEPTION)
line = document.text
if line and not line.isspace():
try:
# Eval and print.
self._execute(line)
except KeyboardInterrupt as e: # KeyboardInterrupt doesn't inherit from Exception.
self._handle_keyboard_interrupt(e)
except Exception as e:
self._handle_exception(e)
self.current_statement_index += 1
except Exit:
pass
def _execute_startup(self, startup_paths):
"""
Load and execute startup file.
"""
if startup_paths:
for path in startup_paths:
with open(path, 'r') as f:
code = compile(f.read(), path, 'exec')
exec_(code, self.globals, self.locals)
def _execute(self, line):
"""
Evaluate the line and print the result.
"""
if line[0:1] == '!':
# Run as shell command
os.system(line[1:])
else:
# Try eval first
try:
result = eval(line, self.globals, self.locals)
self.locals['_'] = self.locals['_%i' % self.current_statement_index] = result
if result is not None:
try:
self.stdout.write('Out[%i]: %r\n' % (self.current_statement_index, result))
except UnicodeDecodeError:
# In Python 2: `__repr__` should return a bytestring,
# so to put it in a unicode context could raise an
# exception that the 'ascii' codec can't decode certain
# characters. Decode as utf-8 in that case.
self.stdout.write('Out[%i]: %s\n' % (self.current_statement_index, repr(result).decode('utf-8')))
# If not a valid `eval` expression, run using `exec` instead.
except SyntaxError:
exec_(line, self.globals, self.locals)
self.stdout.write('\n')
self.stdout.flush()
def _handle_exception(self, e):
# Instead of just calling ``traceback.format_exc``, we take the
# traceback and skip the bottom calls of this framework.
t, v, tb = sys.exc_info()
tblist = traceback.extract_tb(tb)[3:]
l = traceback.format_list(tblist)
if l:
l.insert(0, "Traceback (most recent call last):\n")
l.extend(traceback.format_exception_only(t, v))
tb = ''.join(l)
# Format exception and write to output.
self.stdout.write(highlight(tb, PythonTracebackLexer(), Terminal256Formatter()))
self.stdout.write('%s\n\n' % e)
self.stdout.flush()
def _handle_keyboard_interrupt(self, e):
self.stdout.write('\rKeyboardInterrupt\n\n')
self.stdout.flush()
def embed(globals=None, locals=None, vi_mode=False, history_filename=None, no_colors=False,
autocompletion_style=AutoCompletionStyle.POPUP_MENU, startup_paths=None, always_multiline=False):
"""
Call this to embed Python shell at the current point in your program.
It's similar to `IPython.embed` and `bpython.embed`. ::
from prompt_toolkit.contrib.repl import embed
embed(globals(), locals(), vi_mode=False)
:param vi_mode: Boolean. Use Vi instead of Emacs key bindings.
"""
cli = PythonRepl(globals, locals, vi_mode=vi_mode, history_filename=history_filename,
style=(None if no_colors else PythonStyle),
autocompletion_style=autocompletion_style, always_multiline=always_multiline)
cli.start_repl(startup_paths=startup_paths)