
The interactive interpreter is overwriting the exit and quit builtins with an instance of LLDBQuitter in order to make exit and quit behave like exit() and quit(). It does that by overwriting the __repr__ function to call itself. Despite being a neat trick, it has the unintentional side effect that printing these builtins now quits the interpreter: (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D. >>> print(exit) (lldb) You might consider the above example slightly convoluted, but a more realistic situation is calling locals(): (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D. >>> locals() (lldb) This patch keeps the existing behavior but without overwriting the builtins. Instead, it looks for quit and exit in the input. If they're present, we exit the interpreter with the help of an exception. The previous implementation also used globals to differentiate between exit getting called from the interactive interpreter or from inside a script. This patch achieves the same by using a different exception in for the interpreter case. rdar://84095490 Differential revision: https://reviews.llvm.org/D127895
127 lines
3.7 KiB
Python
127 lines
3.7 KiB
Python
import sys
|
|
if sys.version_info[0] < 3:
|
|
import __builtin__ as builtins
|
|
else:
|
|
import builtins
|
|
import code
|
|
import lldb
|
|
import traceback
|
|
|
|
try:
|
|
import readline
|
|
import rlcompleter
|
|
except ImportError:
|
|
have_readline = False
|
|
except AttributeError:
|
|
# This exception gets hit by the rlcompleter when Linux is using
|
|
# the readline suppression import.
|
|
have_readline = False
|
|
else:
|
|
have_readline = True
|
|
if 'libedit' in readline.__doc__:
|
|
readline.parse_and_bind('bind ^I rl_complete')
|
|
else:
|
|
readline.parse_and_bind('tab: complete')
|
|
|
|
# When running one line, we might place the string to run in this string
|
|
# in case it would be hard to correctly escape a string's contents
|
|
|
|
g_run_one_line_str = None
|
|
|
|
|
|
def get_terminal_size(fd):
|
|
try:
|
|
import fcntl
|
|
import termios
|
|
import struct
|
|
hw = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
|
|
except:
|
|
hw = (0, 0)
|
|
return hw
|
|
|
|
|
|
class LLDBExit(SystemExit):
|
|
pass
|
|
|
|
|
|
def strip_and_check_exit(line):
|
|
line = line.rstrip()
|
|
if line in ('exit', 'quit'):
|
|
raise LLDBExit
|
|
return line
|
|
|
|
|
|
def readfunc(prompt):
|
|
line = input(prompt)
|
|
return strip_and_check_exit(line)
|
|
|
|
|
|
def readfunc_stdio(prompt):
|
|
sys.stdout.write(prompt)
|
|
sys.stdout.flush()
|
|
line = sys.stdin.readline()
|
|
# Readline always includes a trailing newline character unless the file
|
|
# ends with an incomplete line. An empty line indicates EOF.
|
|
if not line:
|
|
raise EOFError
|
|
return strip_and_check_exit(line)
|
|
|
|
|
|
def run_python_interpreter(local_dict):
|
|
# Pass in the dictionary, for continuity from one session to the next.
|
|
try:
|
|
fd = sys.stdin.fileno()
|
|
interacted = False
|
|
if get_terminal_size(fd)[1] == 0:
|
|
try:
|
|
import termios
|
|
old = termios.tcgetattr(fd)
|
|
if old[3] & termios.ECHO:
|
|
# Need to turn off echoing and restore
|
|
new = termios.tcgetattr(fd)
|
|
new[3] = new[3] & ~termios.ECHO
|
|
try:
|
|
termios.tcsetattr(fd, termios.TCSADRAIN, new)
|
|
interacted = True
|
|
code.interact(
|
|
banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()'.",
|
|
readfunc=readfunc_stdio,
|
|
local=local_dict)
|
|
finally:
|
|
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
|
except:
|
|
pass
|
|
# Don't need to turn off echoing
|
|
if not interacted:
|
|
code.interact(
|
|
banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.",
|
|
readfunc=readfunc_stdio,
|
|
local=local_dict)
|
|
else:
|
|
# We have a real interactive terminal
|
|
code.interact(
|
|
banner="Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.",
|
|
readfunc=readfunc,
|
|
local=local_dict)
|
|
except LLDBExit:
|
|
pass
|
|
except SystemExit as e:
|
|
if e.code:
|
|
print('Script exited with code %s' % e.code)
|
|
|
|
|
|
def run_one_line(local_dict, input_string):
|
|
global g_run_one_line_str
|
|
try:
|
|
input_string = strip_and_check_exit(input_string)
|
|
repl = code.InteractiveConsole(local_dict)
|
|
if input_string:
|
|
repl.runsource(input_string)
|
|
elif g_run_one_line_str:
|
|
repl.runsource(g_run_one_line_str)
|
|
except LLDBExit:
|
|
pass
|
|
except SystemExit as e:
|
|
if e.code:
|
|
print('Script exited with code %s' % e.code)
|