[pypy-commit] pypy py3.5-refactor-sys_exc_info: (arigo, cfbolz, richard around):

cfbolz pypy.commits at gmail.com
Tue Nov 15 13:13:47 EST 2016


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: py3.5-refactor-sys_exc_info
Changeset: r88393:195ff4d7c074
Date: 2016-11-15 18:23 +0000
http://bitbucket.org/pypy/pypy/changeset/195ff4d7c074/

Log:	(arigo, cfbolz, richard around):

	in progress: start a refactoring to store sys_exc_info on the ec,
	instead of having last_exception on each frame. this makes it more
	like CPython and should make it possible to not force every frame
	when an exception is raised.

diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -333,13 +333,12 @@
             tb = tb.next
         self._application_traceback = tb
 
-    def record_context(self, space, frame):
-        """Record a __context__ for this exception if one exists,
-        searching from the current frame.
+    def record_context(self, space, ec):
+        """Record a __context__ for this exception if one exists.
         """
         if self._context_recorded:
             return
-        last = frame._exc_info_unroll(space)
+        last = ec.sys_exc_info()
         try:
             if last is not None:
                 self.chain_exceptions(space, last)
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -28,6 +28,10 @@
     def __init__(self, space):
         self.space = space
         self.topframeref = jit.vref_None
+        # this is exposed to app-level as 'sys.exc_info()'.  At any point in
+        # time it is the exception caught by the topmost 'except ... as e:'
+        # app-level block.
+        self.sys_exc_operror = None
         self.w_tracefunc = None
         self.is_tracing = 0
         self.compiler = space.createcompiler()
@@ -230,23 +234,10 @@
         # NOTE: the result is not the wrapped sys.exc_info() !!!
 
         """
-        return self.gettopframe()._exc_info_unroll(self.space)
+        return self.sys_exc_operror
 
     def set_sys_exc_info(self, operror):
-        frame = self.gettopframe_nohidden()
-        if frame:     # else, the exception goes nowhere and is lost
-            frame.last_exception = operror
-
-    def clear_sys_exc_info(self):
-        # Find the frame out of which sys_exc_info() would return its result,
-        # and hack this frame's last_exception to become the cleared
-        # OperationError (which is different from None!).
-        frame = self.gettopframe_nohidden()
-        while frame:
-            if frame.last_exception is not None:
-                frame.last_exception = get_cleared_operation_error(self.space)
-                break
-            frame = self.getnextframe_nohidden(frame)
+        self.sys_exc_operror = operror
 
     @jit.dont_look_inside
     def settrace(self, w_func):
@@ -356,7 +347,6 @@
                     event == 'c_exception'):
                 return
 
-            last_exception = frame.last_exception
             if event == 'leaveframe':
                 event = 'return'
 
@@ -372,7 +362,6 @@
                     raise
 
             finally:
-                frame.last_exception = last_exception
                 self.is_tracing -= 1
 
     def checksignals(self):
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -66,7 +66,6 @@
     f_generator_wref         = rweakref.dead_ref  # for generators/coroutines
     f_generator_nowref       = None               # (only one of the two attrs)
     last_instr               = -1
-    last_exception           = None
     f_backref                = jit.vref_None
     
     escaped                  = False  # see mark_as_escaped()
@@ -328,10 +327,6 @@
                                             executioncontext)
             finally:
                 executioncontext.return_trace(self, w_exitvalue)
-            # it used to say self.last_exception = None
-            # this is now done by the code in pypyjit module
-            # since we don't want to invalidate the virtualizable
-            # for no good reason
             got_exception = False
         finally:
             executioncontext.leave(self, w_exitvalue, got_exception)
@@ -882,33 +877,6 @@
     def fdel_f_trace(self, space):
         self.getorcreatedebug().w_f_trace = None
 
-    def fget_f_exc_type(self, space):
-        if self.last_exception is not None:
-            f = self.f_backref()
-            while f is not None and f.last_exception is None:
-                f = f.f_backref()
-            if f is not None:
-                return f.last_exception.w_type
-        return space.w_None
-
-    def fget_f_exc_value(self, space):
-        if self.last_exception is not None:
-            f = self.f_backref()
-            while f is not None and f.last_exception is None:
-                f = f.f_backref()
-            if f is not None:
-                return f.last_exception.get_w_value(space)
-        return space.w_None
-
-    def fget_f_exc_traceback(self, space):
-        if self.last_exception is not None:
-            f = self.f_backref()
-            while f is not None and f.last_exception is None:
-                f = f.f_backref()
-            if f is not None:
-                return space.wrap(f.last_exception.get_traceback())
-        return space.w_None
-
     def fget_f_restricted(self, space):
         if space.config.objspace.honor__builtins__:
             return space.wrap(self.builtin is not space.builtin)
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -73,7 +73,7 @@
         try:
             next_instr = self.dispatch_bytecode(co_code, next_instr, ec)
         except OperationError as operr:
-            operr.record_context(self.space, self)
+            operr.record_context(self.space, ec)
             next_instr = self.handle_operation_error(ec, operr)
         except RaiseWithExplicitTraceback as e:
             next_instr = self.handle_operation_error(ec, e.operr,
@@ -688,12 +688,11 @@
         if nbargs > 2:
             raise BytecodeCorruption("bad RAISE_VARARGS oparg")
         if nbargs == 0:
-            last_operr = self._exc_info_unroll(space, for_hidden=True)
+            last_operr = self.space.getexecutioncontext().sys_exc_info()
             if last_operr is None:
                 raise oefmt(space.w_RuntimeError,
                             "No active exception to reraise")
             # re-raise, no new traceback obj will be attached
-            self.last_exception = last_operr
             raise RaiseWithExplicitTraceback(last_operr)
         if nbargs == 2:
             w_cause = self.popvalue()
@@ -747,14 +746,20 @@
             self.setdictscope(w_locals)
 
     def POP_EXCEPT(self, oparg, next_instr):
-        block = self.pop_block()
-        block.cleanup(self)
-        return
+        pass    # no-op for now: done by the END_FINALLY that follows anyway
 
     def POP_BLOCK(self, oparg, next_instr):
         block = self.pop_block()
         block.cleanup(self)  # the block knows how to clean up the value stack
 
+    def save_and_change_sys_exc_info(self, operationerr):
+        ec = self.space.getexecutioncontext()
+        last_exception = ec.sys_exc_info()
+        block = SysExcInfoRestorer(last_exception, self.lastblock)
+        self.lastblock = block
+        if operationerr is not None:   # otherwise, don't change sys_exc_info
+            ec.set_sys_exc_info(operationerr)
+
     def end_finally(self):
         # unlike CPython, there are two statically distinct cases: the
         # END_FINALLY might be closing an 'except' block or a 'finally'
@@ -763,8 +768,14 @@
         #   [exception value we are now handling]
         #   [wrapped SApplicationException]
         # In the case of a finally: block, the stack contains only one
-        # item (unlike CPython which can have 1, 2 or 3 items):
+        # item (unlike CPython which can have 1, 2, 3 or 5 items, and
+        # even in one case a non-fixed number of items):
         #   [wrapped subclass of SuspendedUnroller]
+
+        block = self.pop_block()
+        assert isinstance(block, SysExcInfoRestorer)
+        block.cleanupstack()   # restores ec.sys_exc_operror
+
         w_top = self.popvalue()
         if self.space.is_w(w_top, self.space.w_None):
             # case of a finally: block with no exception
@@ -1137,8 +1148,8 @@
         w_exit = self.space.get(w_descr, w_manager)
         self.settopvalue(w_exit)
         w_result = self.space.get_and_call_function(w_enter, w_manager)
-        block = WithBlock(self.valuestackdepth,
-                          next_instr + offsettoend, self.lastblock)
+        block = FinallyBlock(self.valuestackdepth,
+                             next_instr + offsettoend, self.lastblock)
         self.lastblock = block
         self.pushvalue(w_result)
 
@@ -1150,6 +1161,7 @@
         if isinstance(w_unroller, SApplicationException):
             # app-level exception
             operr = w_unroller.operr
+            # this looks again like the kind of code we have. except that it's a call, not a block
             old_last_exception = self.last_exception
             self.last_exception = operr
             w_traceback = self.space.wrap(operr.get_traceback())
@@ -1183,6 +1195,7 @@
             if self.space.is_true(w_suppress):
                 # __exit__() returned True -> Swallow the exception.
                 self.settopvalue(self.space.w_None)
+        # this is always followed by END_FINALLY
 
     @jit.unroll_safe
     def call_function(self, oparg, w_starstar=None, has_vararg=False):
@@ -1680,28 +1693,26 @@
             return r_uint(self.handlerposition)
 
 
-class ExceptHandlerBlock(FrameBlock):
+class SysExcInfoRestorer(FrameBlock):
     """
-    This is a special, implicit block type which is created when entering an
-    except handler. It does not belong to any opcode
+    This is a special, implicit block type which is created when entering a
+    finally or except handler. It does not belong to any opcode
     """
 
     _immutable_ = True
-    _opname = 'EXCEPT_HANDLER_BLOCK' # it's not associated to any opcode
+    _opname = 'SYS_EXC_INFO_RESTORER' # it's not associated to any opcode
     handling_mask = 0 # this block is never handled, only popped by POP_EXCEPT
 
+    def __init__(self, operr, previous):
+        self.operr = operr
+        self.previous = previous
+
     def handle(self, frame, unroller):
         assert False # never called
 
     def cleanupstack(self, frame):
-        frame.dropvaluesuntil(self.valuestackdepth+1)
-        w_last_exception = frame.popvalue()
-        if not isinstance(w_last_exception, W_OperationError):
-            msg = "expected an OperationError, got %s" % (
-                frame.space.str_w(w_last_exception))
-            raise BytecodeCorruption(msg)
-        frame.last_exception = w_last_exception.operr
-        FrameBlock.cleanupstack(self, frame)
+        ec = frame.space.getexecutioncontext()
+        ec.set_sys_exc_info(self.operr)
 
 
 class ExceptBlock(FrameBlock):
@@ -1715,22 +1726,18 @@
         # push the exception to the value stack for inspection by the
         # exception handler (the code after the except:)
         self.cleanupstack(frame)
+        # the stack setup is slightly different than in CPython:
+        # instead of the traceback, we store the unroller object,
+        # wrapped.
         assert isinstance(unroller, SApplicationException)
         operationerr = unroller.operr
         operationerr.normalize_exception(frame.space)
-        # the stack setup is slightly different than in CPython:
-        # instead of the traceback, we store the unroller object,
-        # wrapped.
-        # this is popped by POP_EXCEPT, which is present only in py3k
-        w_last_exception = W_OperationError(frame.last_exception)
-        w_last_exception = frame.space.wrap(w_last_exception)
-        frame.pushvalue(w_last_exception)
-        block = ExceptHandlerBlock(self.valuestackdepth, 0, frame.lastblock)
-        frame.lastblock = block
         frame.pushvalue(frame.space.wrap(unroller))
         frame.pushvalue(operationerr.get_w_value(frame.space))
         frame.pushvalue(operationerr.w_type)
-        frame.last_exception = operationerr
+        # set the current value of sys_exc_info to operationerr,
+        # saving the old value in a custom type of FrameBlock
+        frame.save_and_change_sys_exc_info(operationerr)
         return r_uint(self.handlerposition)   # jump to the handler
 
 
@@ -1740,7 +1747,6 @@
     _immutable_ = True
     _opname = 'SETUP_FINALLY'
     handling_mask = -1     # handles every kind of SuspendedUnroller
-    restore_last_exception = True # set to False by WithBlock
 
     def handle(self, frame, unroller):
         # any abnormal reason for unrolling a finally: triggers the end of
@@ -1752,53 +1758,44 @@
             operationerr = unroller.operr
             operationerr.normalize_exception(frame.space)
         frame.pushvalue(frame.space.wrap(unroller))
-        if operationerr and self.restore_last_exception:
-            frame.last_exception = operationerr
+        # set the current value of sys_exc_info to operationerr,
+        # saving the old value in a custom type of FrameBlock
+        frame.save_and_change_sys_exc_info(operationerr)
         return r_uint(self.handlerposition)   # jump to the handler
 
 
-class WithBlock(FinallyBlock):
-
-    _immutable_ = True
-    restore_last_exception = False
-
-    def handle(self, frame, unroller):
-        if isinstance(unroller, SApplicationException):
-            unroller.operr.normalize_exception(frame.space)
-        return FinallyBlock.handle(self, frame, unroller)
-
-block_classes = {'EXCEPT_HANDLER_BLOCK': ExceptHandlerBlock,
+block_classes = {'SYS_EXC_INFO_RESTORER': SysExcInfoRestorer,
                  'SETUP_LOOP': LoopBlock,
                  'SETUP_EXCEPT': ExceptBlock,
                  'SETUP_FINALLY': FinallyBlock,
-                 'SETUP_WITH': WithBlock,
+                 'SETUP_WITH': FinallyBlock,
                  }
 
 
-class W_OperationError(W_Root):
-    """
-    Tiny applevel wrapper around an OperationError.
-    """
-
-    def __init__(self, operr):
-        self.operr = operr
-
-    def descr_reduce(self, space):
-        from pypy.interpreter.mixedmodule import MixedModule
-        w_mod = space.getbuiltinmodule('_pickle_support')
-        mod = space.interp_w(MixedModule, w_mod)
-        w_new_inst = mod.get('operationerror_new')
-        w_args = space.newtuple([])
-        operr = self.operr
-        if operr is None:
-            return space.newtuple([w_new_inst, w_args])
-        w_state = space.newtuple([operr.w_type, operr.get_w_value(space),
-                                  operr.get_traceback()])
-        return space.newtuple([w_new_inst, w_args, w_state])
-
-    def descr_setstate(self, space, w_state):
-        w_type, w_value, w_tb = space.fixedview(w_state, 3)
-        self.operr = OperationError(w_type, w_value, w_tb)
+##class W_OperationError(W_Root):
+##    """
+##    Tiny applevel wrapper around an OperationError.
+##    """
+##
+##    def __init__(self, operr):
+##        self.operr = operr
+##
+##    def descr_reduce(self, space):
+##        from pypy.interpreter.mixedmodule import MixedModule
+##        w_mod = space.getbuiltinmodule('_pickle_support')
+##        mod = space.interp_w(MixedModule, w_mod)
+##        w_new_inst = mod.get('operationerror_new')
+##        w_args = space.newtuple([])
+##        operr = self.operr
+##        if operr is None:
+##            return space.newtuple([w_new_inst, w_args])
+##        w_state = space.newtuple([operr.w_type, operr.get_w_value(space),
+##                                  operr.get_traceback()])
+##        return space.newtuple([w_new_inst, w_args, w_state])
+##
+##    def descr_setstate(self, space, w_state):
+##        w_type, w_value, w_tb = space.fixedview(w_state, 3)
+##        self.operr = OperationError(w_type, w_value, w_tb)
 
 def source_as_str(space, w_source, funcname, what, flags):
     """Return source code as str0 with adjusted compiler flags
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -464,7 +464,7 @@
 from pypy.interpreter.eval import Code
 from pypy.interpreter.pycode import PyCode, CO_VARARGS, CO_VARKEYWORDS
 from pypy.interpreter.pyframe import PyFrame
-from pypy.interpreter.pyopcode import SuspendedUnroller, W_OperationError
+from pypy.interpreter.pyopcode import SuspendedUnroller
 from pypy.interpreter.module import Module
 from pypy.interpreter.function import (Function, Method, StaticMethod,
     ClassMethod, BuiltinFunction, descr_function_get)
@@ -616,9 +616,6 @@
     f_lasti = GetSetProperty(PyFrame.fget_f_lasti),
     f_trace = GetSetProperty(PyFrame.fget_f_trace, PyFrame.fset_f_trace,
                              PyFrame.fdel_f_trace),
-    f_exc_type = GetSetProperty(PyFrame.fget_f_exc_type),
-    f_exc_value = GetSetProperty(PyFrame.fget_f_exc_value),
-    f_exc_traceback = GetSetProperty(PyFrame.fget_f_exc_traceback),
     f_restricted = GetSetProperty(PyFrame.fget_f_restricted),
     f_code = GetSetProperty(PyFrame.fget_code),
     f_locals = GetSetProperty(PyFrame.fget_getdictscope),
@@ -878,8 +875,8 @@
 SuspendedUnroller.typedef = TypeDef("SuspendedUnroller")
 SuspendedUnroller.typedef.acceptable_as_base_class = False
 
-W_OperationError.typedef = TypeDef("OperationError",
-    __reduce__ = interp2app(W_OperationError.descr_reduce),
-    __setstate__ = interp2app(W_OperationError.descr_setstate),
-)
-W_OperationError.typedef.acceptable_as_base_class = False
+## W_OperationError.typedef = TypeDef("OperationError",
+##     __reduce__ = interp2app(W_OperationError.descr_reduce),
+##     __setstate__ = interp2app(W_OperationError.descr_setstate),
+## )
+## W_OperationError.typedef.acceptable_as_base_class = False


More information about the pypy-commit mailing list