[Lldb-commits] [lldb] 6cde6ac - [lldb] Don't overwrite quit and exit builtins in the Python interpreter

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Wed Jun 15 14:53:45 PDT 2022


Author: Jonas Devlieghere
Date: 2022-06-15T14:53:40-07:00
New Revision: 6cde6ac03c2c1851b1156dd334b87b38fff79f70

URL: https://github.com/llvm/llvm-project/commit/6cde6ac03c2c1851b1156dd334b87b38fff79f70
DIFF: https://github.com/llvm/llvm-project/commit/6cde6ac03c2c1851b1156dd334b87b38fff79f70.diff

LOG: [lldb] Don't overwrite quit and exit builtins in the Python interpreter

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

Added: 
    lldb/test/Shell/ScriptInterpreter/Python/exit.test

Modified: 
    lldb/source/Interpreter/embedded_interpreter.py

Removed: 
    


################################################################################
diff  --git a/lldb/source/Interpreter/embedded_interpreter.py b/lldb/source/Interpreter/embedded_interpreter.py
index 9312dbfaca4e1..523bc051a0ce2 100644
--- a/lldb/source/Interpreter/embedded_interpreter.py
+++ b/lldb/source/Interpreter/embedded_interpreter.py
@@ -1,4 +1,4 @@
-import sys
+import sys
 if sys.version_info[0] < 3:
     import __builtin__ as builtins
 else:
@@ -23,36 +23,6 @@
     else:
         readline.parse_and_bind('tab: complete')
 
-g_builtin_override_called = False
-
-
-class LLDBQuitter(object):
-
-    def __init__(self, name):
-        self.name = name
-
-    def __repr__(self):
-        self()
-
-    def __call__(self, code=None):
-        global g_builtin_override_called
-        g_builtin_override_called = True
-        raise SystemExit(-1)
-
-
-def setquit():
-    '''Redefine builtin functions 'quit()' and 'exit()' to print a message and raise an EOFError exception.'''
-    # This function will be called prior to each interactive
-    # interpreter loop or each single line, so we set the global
-    # g_builtin_override_called to False so we know if a SystemExit
-    # is thrown, we can catch it and tell the 
diff erence between
-    # a call to "quit()" or "exit()" and something like
-    # "sys.exit(123)"
-    global g_builtin_override_called
-    g_builtin_override_called = False
-    builtins.quit = LLDBQuitter('quit')
-    builtins.exit = LLDBQuitter('exit')
-
 # 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
 
@@ -70,6 +40,22 @@ def get_terminal_size(fd):
     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()
@@ -78,12 +64,11 @@ def readfunc_stdio(prompt):
     # ends with an incomplete line. An empty line indicates EOF.
     if not line:
         raise EOFError
-    return line.rstrip()
+    return strip_and_check_exit(line)
 
 
 def run_python_interpreter(local_dict):
     # Pass in the dictionary, for continuity from one session to the next.
-    setquit()
     try:
         fd = sys.stdin.fileno()
         interacted = False
@@ -116,24 +101,26 @@ def run_python_interpreter(local_dict):
             # 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:
-        global g_builtin_override_called
-        if not g_builtin_override_called:
-            print('Script exited with %s' % (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
-    setquit()
     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:
-        global g_builtin_override_called
-        if not g_builtin_override_called:
-            print('Script exited with %s' % (e))
+        if e.code:
+            print('Script exited with code %s' % e.code)

diff  --git a/lldb/test/Shell/ScriptInterpreter/Python/exit.test b/lldb/test/Shell/ScriptInterpreter/Python/exit.test
new file mode 100644
index 0000000000000..9895dc18bb7de
--- /dev/null
+++ b/lldb/test/Shell/ScriptInterpreter/Python/exit.test
@@ -0,0 +1,27 @@
+# RUN: %lldb -o 'script quit' | FileCheck %s --check-prefix SILENT
+# RUN: %lldb -o 'script quit()' | FileCheck %s --check-prefix SILENT
+
+# RUN: %lldb -o 'script exit' | FileCheck %s --check-prefix SILENT
+# RUN: %lldb -o 'script exit()' | FileCheck %s --check-prefix SILENT
+
+# RUN: echo -e 'script\nquit' > %t
+# RUN: cat %t | %lldb | FileCheck %s --check-prefix SILENT
+
+# RUN: echo -e 'script\nexit' > %t
+# RUN: cat %t | %lldb | FileCheck %s --check-prefix SILENT
+
+# SILENT-NOT: Script exited with code
+
+# RUN: %lldb -o 'script quit(100+23)' | FileCheck %s --check-prefix VERBOSE
+# RUN: %lldb -o 'script exit(100+23)' | FileCheck %s --check-prefix VERBOSE
+
+# RUN: echo -e 'script\nexit(100+23)' > %t
+# RUN: cat %t | %lldb | FileCheck %s --check-prefix VERBOSE
+
+# RUN: echo -e 'script\nquit(100+23)' > %t
+# RUN: cat %t | %lldb | FileCheck %s --check-prefix VERBOSE
+
+# VERBOSE: Script exited with code 123
+
+# RUN: %lldb -o 'script print(locals())' | FileCheck %s --check-prefix LOCALS
+# LOCALS: __builtins__


        


More information about the lldb-commits mailing list