[pypy-commit] pypy py3.6: Merged in py3.6-exc-info-2 (pull request #686)

rlamy pypy.commits at gmail.com
Thu Dec 12 08:01:43 EST 2019


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3.6
Changeset: r98277:357a713081c3
Date: 2019-12-12 12:59 +0000
http://bitbucket.org/pypy/pypy/changeset/357a713081c3/

Log:	Merged in py3.6-exc-info-2 (pull request #686)

	Fix handling of sys.exc_info() in generators

diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -34,7 +34,6 @@
         # time it is the exception caught by the topmost 'except ... as e:'
         # app-level block.
         self.sys_exc_operror = None
-        self.previous_operror_stack = []
         self.w_tracefunc = None
         self.is_tracing = 0
         self.compiler = space.createcompiler()
@@ -248,15 +247,7 @@
         # NOTE: the result is not the wrapped sys.exc_info() !!!
 
         """
-        result = self.sys_exc_operror
-        if result is None:
-            i = len(self.previous_operror_stack) - 1
-            while i >= 0:
-                result = self.previous_operror_stack[i]
-                if result is not None:
-                    break
-                i -= 1
-        return result
+        return self.sys_exc_operror
 
     def set_sys_exc_info(self, operror):
         self.sys_exc_operror = operror
@@ -277,26 +268,6 @@
             operror = OperationError(w_type, w_value, tb)
         self.set_sys_exc_info(operror)
 
-    def enter_error_stack_item(self, saved_operr):
-        # 'sys_exc_operror' should be logically considered as the last
-        # item on the stack, so pushing a new item has the following effect:
-        self.previous_operror_stack.append(self.sys_exc_operror)
-        self.sys_exc_operror = saved_operr
-
-    def leave_error_stack_item(self):
-        result = self.sys_exc_operror
-        self.sys_exc_operror = self.previous_operror_stack.pop()
-        return result
-
-    def fetch_and_clear_error_stack_state(self):
-        result = self.sys_exc_operror, self.previous_operror_stack
-        self.sys_exc_operror = None
-        self.previous_operror_stack = []
-        return result
-
-    def restore_error_stack_state(self, saved):
-        self.sys_exc_operror, self.previous_operror_stack = saved
-
     @jit.dont_look_inside
     def settrace(self, w_func):
         """Set the global trace function."""
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -119,7 +119,10 @@
                             "can't send non-None value to a just-started %s",
                             self.KIND)
         ec = space.getexecutioncontext()
-        ec.enter_error_stack_item(self.saved_operr)
+        current_exc_info = ec.sys_exc_info()
+        if self.saved_operr is not None:
+            ec.set_sys_exc_info(self.saved_operr)
+            self.saved_operr = None
         self.running = True
         try:
             w_result = frame.execute_frame(w_arg_or_err)
@@ -140,7 +143,9 @@
             # note: this is not perfectly correct: see
             # test_exc_info_in_generator_4.  But it's simpler and
             # bug-to-bug compatible with CPython 3.5 and 3.6.
-            self.saved_operr = ec.leave_error_stack_item()
+            if frame._any_except_or_finally_handler():
+                self.saved_operr = ec.sys_exc_info()
+            ec.set_sys_exc_info(current_exc_info)
         return w_result
 
     def get_delegate(self):
diff --git a/pypy/interpreter/test/apptest_generator.py b/pypy/interpreter/test/apptest_generator.py
--- a/pypy/interpreter/test/apptest_generator.py
+++ b/pypy/interpreter/test/apptest_generator.py
@@ -1,5 +1,7 @@
 from pytest import raises, skip
 
+import sys
+
 def test_generator():
     def f():
         yield 1
@@ -462,7 +464,6 @@
     assert closed == [True]
 
 def test_exc_info_in_generator():
-    import sys
     def g():
         try:
             raise ValueError
@@ -533,8 +534,35 @@
     try:
         raise IndexError
     except IndexError:
-        assert next(gen) is 1
-    assert next(gen) is 2
+        assert next(gen) == 1
+    assert next(gen) == 2
+
+def test_except_gen_except():
+    def gen():
+        try:
+            assert sys.exc_info()[0] is None
+            yield
+            # we are called from "except ValueError:", TypeError must
+            # inherit ValueError in its context
+            raise TypeError()
+        except TypeError as exc:
+            assert sys.exc_info()[0] is TypeError
+            assert type(exc.__context__) is ValueError
+        # here we are still called from the "except ValueError:"
+        assert sys.exc_info()[0] is ValueError
+        yield
+        assert sys.exc_info()[0] is None
+        yield "done"
+
+    g = gen()
+    next(g)
+    try:
+        raise ValueError
+    except Exception:
+        next(g)
+
+    assert next(g) == "done"
+    assert sys.exc_info() == (None, None, None)
 
 def test_multiple_invalid_sends():
     def mygen():
@@ -793,13 +821,9 @@
             yield from map(operator.truediv, [2, 3], [4, 0])
     gen = f()
     assert next(gen) == 0.5
-    try:
+    with raises(ZeroDivisionError) as excinfo:
         next(gen)
-    except ZeroDivisionError as e:
-        assert e.__context__ is not None
-        assert isinstance(e.__context__, ValueError)
-    else:
-        assert False, "should have raised"
+    assert isinstance(excinfo.value.__context__, ValueError)
 
 
 def test_past_generator_stop():
diff --git a/pypy/module/_continuation/interp_continuation.py b/pypy/module/_continuation/interp_continuation.py
--- a/pypy/module/_continuation/interp_continuation.py
+++ b/pypy/module/_continuation/interp_continuation.py
@@ -46,9 +46,9 @@
         #
         global_state.origin = self
         self.sthread = sthread
-        saved_error_state = pre_switch(sthread)
+        saved_exception = pre_switch(sthread)
         h = sthread.new(new_stacklet_callback)
-        post_switch(sthread, h, saved_error_state)
+        post_switch(sthread, h, saved_exception)
 
     def switch(self, w_to):
         sthread = self.sthread
@@ -84,9 +84,9 @@
             # double switch: the final destination is to.h
             global_state.destination = to
         #
-        saved_error_state = pre_switch(sthread)
+        saved_exception = pre_switch(sthread)
         h = sthread.switch(global_state.destination.h)
-        return post_switch(sthread, h, saved_error_state)
+        return post_switch(sthread, h, saved_exception)
 
     @unwrap_spec(w_value = WrappedDefault(None),
                  w_to = WrappedDefault(None))
@@ -257,9 +257,11 @@
     return self.h
 
 def pre_switch(sthread):
-    return sthread.ec.fetch_and_clear_error_stack_state()
+    saved_exception = sthread.ec.sys_exc_info()
+    sthread.ec.set_sys_exc_info(None)
+    return saved_exception
 
-def post_switch(sthread, h, saved_error_state):
+def post_switch(sthread, h, saved_exception):
     origin = global_state.origin
     self = global_state.destination
     global_state.origin = None
@@ -268,7 +270,7 @@
     #
     current = sthread.ec.topframeref
     sthread.ec.topframeref = self.bottomframe.f_backref
-    sthread.ec.restore_error_stack_state(saved_error_state)
+    sthread.ec.set_sys_exc_info(saved_exception)
     self.bottomframe.f_backref = origin.bottomframe.f_backref
     origin.bottomframe.f_backref = current
     #


More information about the pypy-commit mailing list