[pypy-commit] pypy py3.5: Add operror.chain_exception(), to add a __context__ from rpython code.

amauryfa pypy.commits at gmail.com
Sun Nov 13 14:44:23 EST 2016


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3.5
Changeset: r88363:e1ddd470a8ac
Date: 2016-11-12 20:45 +0100
http://bitbucket.org/pypy/pypy/changeset/e1ddd470a8ac/

Log:	Add operror.chain_exception(), to add a __context__ from rpython
	code.

	But this can call normalize_exception() more often. Move w_cause out
	of normalize_exception(), to not overwrite the "suppress_context"
	flag.

diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -33,12 +33,10 @@
     _w_value = None
     _application_traceback = None
     _context_recorded = False
-    w_cause = None
 
-    def __init__(self, w_type, w_value, tb=None, w_cause=None):
+    def __init__(self, w_type, w_value, tb=None):
         self.setup(w_type, w_value)
         self._application_traceback = tb
-        self.w_cause = w_cause
 
     def setup(self, w_type, w_value=None):
         assert w_type is not None
@@ -215,13 +213,6 @@
                         # raise Type, X: assume X is the constructor argument
                         w_value = space.call_function(w_type, w_value)
                     w_type = self._exception_getclass(space, w_value)
-            if self.w_cause:
-                # ensure w_cause is of a valid type
-                if space.is_none(self.w_cause):
-                    pass
-                else:
-                    self._exception_getclass(space, self.w_cause, "exception causes")
-                space.setattr(w_value, space.wrap("__cause__"), self.w_cause)
             if self._application_traceback:
                 from pypy.interpreter.pytraceback import PyTraceback
                 from pypy.module.exceptions.interp_exceptions import W_BaseException
@@ -318,6 +309,17 @@
             tb.frame.mark_as_escaped()
         return tb
 
+    def set_cause(self, space, w_cause):
+        if w_cause is None:
+            return
+        # ensure w_cause is of a valid type
+        if space.is_none(w_cause):
+            pass
+        else:
+            self._exception_getclass(space, w_cause, "exception causes")
+        w_value = self.get_w_value(space)
+        space.setattr(w_value, space.wrap("__cause__"), w_cause)
+
     def set_traceback(self, traceback):
         """Set the current traceback."""
         self._application_traceback = traceback
@@ -340,15 +342,20 @@
         last = frame._exc_info_unroll(space)
         try:
             if last is not None:
-                self.normalize_exception(space)
-                w_value = self.get_w_value(space)
-                w_last = last.get_w_value(space)
-                if not space.is_w(w_value, w_last):
-                    _break_context_cycle(space, w_value, w_last)
-                    space.setattr(w_value, space.wrap('__context__'), w_last)
+                self.chain_exceptions(space, last)
         finally:
             self._context_recorded = True
 
+    def chain_exceptions(self, space, context):
+        """Attach another OperationError as __context__."""
+        self.normalize_exception(space)
+        w_value = self.get_w_value(space)
+        context.normalize_exception(space)
+        w_context = context.get_w_value(space)
+        if not space.is_w(w_value, w_context):
+            _break_context_cycle(space, w_value, w_context)
+            space.setattr(w_value, space.wrap('__context__'), w_context)
+
     # A simplified version of _PyErr_TrySetFromCause, which returns a
     # new exception of the same class, but with another error message.
     # This only works for exceptions which have just a single message,
@@ -376,8 +383,8 @@
         try:
             new_error = oefmt(space.type(w_value),
                               "%s (%T: %S)", message, w_value, w_value)
-            new_error.w_cause = w_value
             new_error.normalize_exception(space)
+            new_error.set_cause(space, w_value)
             # Copy the traceback, but it does not escape.
             new_error.set_traceback(self._application_traceback)
         except OperationError:
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -224,8 +224,8 @@
                                    consts.CO_ITERABLE_COROUTINE):
             e2 = OperationError(space.w_RuntimeError,
                                 space.wrap("%s raised StopIteration" %
-                                           self.KIND),
-                                w_cause=e.get_w_value(space))
+                                           self.KIND))
+            e2.chain_exceptions(space, e)
             e2.record_context(space, self.frame)
             raise e2
         else:
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -707,8 +707,9 @@
             w_value = space.call_function(w_type)
         else:
             w_type = space.type(w_value)
-        operror = OperationError(w_type, w_value, w_cause=w_cause)
+        operror = OperationError(w_type, w_value)
         operror.normalize_exception(space)
+        operror.set_cause(space, w_cause)
         tb = space.getattr(w_value, space.wrap('__traceback__'))
         if not space.is_w(tb, space.w_None):
             operror.set_traceback(tb)
diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -302,21 +302,19 @@
         with self.lock:
             if self._closed(space):
                 return
-        w_flush_exception = None
+        flush_operr = None
         try:
             space.call_method(self, "flush")
         except OperationError as e:
-            w_flush_exception = e.get_w_value(space)
+            flush_operr = e
             raise
         finally:
             with self.lock:
                 try:
                     space.call_method(self.w_raw, "close")
                 except OperationError as e:
-                    if w_flush_exception:
-                        space.setattr(e.get_w_value(space),
-                                      space.wrap('__context__'),
-                                      w_flush_exception)
+                    if flush_operr:
+                        e.chain_exceptions(space, flush_operr)
                     raise
 
     def _dealloc_warn_w(self, space, w_source):


More information about the pypy-commit mailing list