[pypy-commit] pypy py3k: avoid triggering the now lazy __context__ setup 'up front' (when grabbing it

pjenvey noreply at buildbot.pypy.org
Wed Jun 18 23:06:33 CEST 2014


Author: Philip Jenvey <pjenvey at underboss.org>
Branch: py3k
Changeset: r72098:3856e33d85ad
Date: 2014-06-18 13:09 -0700
http://bitbucket.org/pypy/pypy/changeset/3856e33d85ad/

Log:	avoid triggering the now lazy __context__ setup 'up front' (when
	grabbing it from the current frame) when breaking __context__ chain
	cycles, for now. this is a trade-off: we won't break some cycles in
	obscure situations for the sake of not paying a cost in probably
	more situations

diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -332,15 +332,16 @@
             # normalize w_value so setup_context can check for cycles
             self.normalize_exception(space)
             w_value = self.get_w_value(space)
-            w_context = setup_context(space, w_value,
-                                      last_exception.get_w_value(space))
+            w_last = last_exception.get_w_value(space)
+            w_context = setup_context(space, w_value, w_last, lazy=True)
             space.setattr(w_value, space.wrap('__context__'), w_context)
 
 
-def setup_context(space, w_exc, w_last):
+def setup_context(space, w_exc, w_last, lazy=False):
     """Determine the __context__ for w_exc from w_last and break
     reference cycles in the __context__ chain.
     """
+    from pypy.module.exceptions.interp_exceptions import W_BaseException
     if space.is_w(w_exc, w_last):
         w_last = space.w_None
     # w_last may also be space.w_None if from ClearedOpErr
@@ -349,15 +350,16 @@
         # O(chain length) but context chains are usually very short.
         w_obj = w_last
         while True:
-            # XXX: __context__ becomes not so lazy when we're forced to
-            # access it here! Could this be defered till later? Or at
-            # least limit the check to W_BaseException.w_context
-            # (avoiding W_BaseException._setup_context)
-            w_context = space.getattr(w_obj, space.wrap('__context__'))
-            if space.is_w(w_context, space.w_None):
+            assert isinstance(w_obj, W_BaseException)
+            if lazy:
+                w_context = w_obj.w_context
+            else:
+                # triggers W_BaseException._setup_context
+                w_context = space.getattr(w_obj, space.wrap('__context__'))
+            if space.is_none(w_context):
                 break
             if space.is_w(w_context, w_exc):
-                space.setattr(w_obj, space.wrap('__context__'), space.w_None)
+                w_obj.w_context = space.w_None
                 break
             w_obj = w_context
     return w_last
diff --git a/pypy/interpreter/test/test_raise.py b/pypy/interpreter/test/test_raise.py
--- a/pypy/interpreter/test/test_raise.py
+++ b/pypy/interpreter/test/test_raise.py
@@ -385,6 +385,27 @@
         except:
             func1()
 
+    @py.test.mark.xfail(reason="A somewhat contrived case that may burden the "
+                        "JIT to fully support")
+    def test_frame_spanning_cycle_broken(self):
+        context = IndexError()
+        def func():
+            try:
+                1/0
+            except Exception as e1:
+                try:
+                    raise context
+                except Exception as e2:
+                    assert e2.__context__ is e1
+                    # XXX:
+                    assert e1.__context__ is None
+            else:
+                fail('No exception raised')
+        try:
+            raise context
+        except:
+            func()
+
 
 class AppTestTraceback:
 


More information about the pypy-commit mailing list