[pypy-commit] pypy release-pypy3.6-v7.x: merge py3.6 into release branch

mattip pypy.commits at gmail.com
Sun Dec 8 02:02:48 EST 2019


Author: Matti Picus <matti.picus at gmail.com>
Branch: release-pypy3.6-v7.x
Changeset: r98255:d6ff09cd0648
Date: 2019-12-08 09:01 +0200
http://bitbucket.org/pypy/pypy/changeset/d6ff09cd0648/

Log:	merge py3.6 into release branch

diff too long, truncating to 2000 out of 3708 lines

diff --git a/lib_pypy/_cffi_ssl/_stdssl/error.py b/lib_pypy/_cffi_ssl/_stdssl/error.py
--- a/lib_pypy/_cffi_ssl/_stdssl/error.py
+++ b/lib_pypy/_cffi_ssl/_stdssl/error.py
@@ -27,13 +27,14 @@
         if self.strerror and isinstance(self.strerror, str):
             return self.strerror
         return str(self.args)
-# these are expected on socket as well
-socket.sslerror = SSLError
-for v in [ 'SSL_ERROR_ZERO_RETURN', 'SSL_ERROR_WANT_READ',
-     'SSL_ERROR_WANT_WRITE', 'SSL_ERROR_WANT_X509_LOOKUP', 'SSL_ERROR_SYSCALL',
-     'SSL_ERROR_SSL', 'SSL_ERROR_WANT_CONNECT', 'SSL_ERROR_EOF',
-     'SSL_ERROR_INVALID_ERROR_CODE' ]:
-    setattr(socket, v, locals()[v]) 
+# these are expected on socket in python2 as well
+if sys.version_info[0] < 3:
+    socket.sslerror = SSLError
+    for v in [ 'SSL_ERROR_ZERO_RETURN', 'SSL_ERROR_WANT_READ',
+         'SSL_ERROR_WANT_WRITE', 'SSL_ERROR_WANT_X509_LOOKUP', 'SSL_ERROR_SYSCALL',
+         'SSL_ERROR_SSL', 'SSL_ERROR_WANT_CONNECT', 'SSL_ERROR_EOF',
+         'SSL_ERROR_INVALID_ERROR_CODE' ]:
+        setattr(socket, v, locals()[v]) 
 
 class SSLZeroReturnError(SSLError):
     """ SSL/TLS session closed cleanly. """
diff --git a/lib_pypy/_curses_build.py b/lib_pypy/_curses_build.py
--- a/lib_pypy/_curses_build.py
+++ b/lib_pypy/_curses_build.py
@@ -28,13 +28,25 @@
     # error message
     raise e_last
 
-def find_curses_include_dirs():
-    if os.path.exists('/usr/include/ncurses'):
-        return ['/usr/include/ncurses']
-    if os.path.exists('/usr/include/ncursesw'):
-        return ['/usr/include/ncursesw']
-    return []
+def find_curses_dir_and_name():
+    for base in ('/usr', '/usr/local'):
+        if os.path.exists(os.path.join(base, 'include', 'ncursesw')):
+            return base, 'ncursesw'
+        if os.path.exists(os.path.join(base, 'include', 'ncurses')):
+            return base, 'ncurses'
+    return '', None
 
+base, name = find_curses_dir_and_name()
+if base:
+    include_dirs = [os.path.join(base, 'include', name)]
+    library_dirs = [os.path.join(base, 'lib')]
+    libs = [name, name.replace('ncurses', 'panel')]
+else:
+    include_dirs = []
+    library_dirs = []
+    libs = [find_library(['ncursesw', 'ncurses']),
+                find_library(['panelw', 'panel']),
+           ]
 
 ffi = FFI()
 ffi.set_source("_curses_cffi", """
@@ -83,9 +95,10 @@
 void _m_getsyx(int *yx) {
     getsyx(yx[0], yx[1]);
 }
-""", libraries=[find_library(['ncurses', 'ncursesw']),
-                find_library(['panel', 'panelw'])],
-     include_dirs=find_curses_include_dirs())
+""", libraries=libs,
+     library_dirs = library_dirs,
+     include_dirs=include_dirs,
+)
 
 
 ffi.cdef("""
diff --git a/pypy/doc/build.rst b/pypy/doc/build.rst
--- a/pypy/doc/build.rst
+++ b/pypy/doc/build.rst
@@ -60,12 +60,9 @@
 Install build-time dependencies
 -------------------------------
 (**Note**: for some hints on how to translate the Python interpreter under
-Windows, see the `windows document`_ . For hints on how to cross-compile in
-a chroot using scratchbox2, see the `arm document`_ in the
-`RPython documentation`_)
+Windows, see the `windows document`_ . 
 
 .. _`windows document`: windows.html
-.. _`arm document`: http://rpython.readthedocs.org/en/latest/arm.html
 .. _`RPython documentation`: http://rpython.readthedocs.org
 
 The host Python needs to have CFFI installed. If translating on PyPy, CFFI is
@@ -88,9 +85,6 @@
 pyexpat
     libexpat1
 
-_ssl
-    libssl
-
 _vmprof
     libunwind (optional, loaded dynamically at runtime)
 
@@ -104,6 +98,9 @@
 sqlite3
     libsqlite3
 
+_ssl, _hashlib
+    libssl
+
 curses
     libncurses-dev   (for PyPy2)
     libncursesw-dev  (for PyPy3)
@@ -115,11 +112,12 @@
     tk-dev
 
 lzma (PyPy3 only)
-    liblzma
+    liblzma or libxz, version 5 and up
 
-To run untranslated tests, you need the Boehm garbage collector libgc.
+To run untranslated tests, you need the Boehm garbage collector libgc, version
+7.4 and up
 
-On recent Debian and Ubuntu (16.04 onwards), this is the command to install
+On Debian and Ubuntu (16.04 onwards), this is the command to install
 all build-time dependencies::
 
     apt-get install gcc make libffi-dev pkg-config zlib1g-dev libbz2-dev \
@@ -127,18 +125,11 @@
     tk-dev libgc-dev python-cffi \
     liblzma-dev libncursesw5-dev     # these two only needed on PyPy3
 
-On older Debian and Ubuntu (12.04-14.04)::
-
-    apt-get install gcc make libffi-dev pkg-config libz-dev libbz2-dev \
-    libsqlite3-dev libncurses-dev libexpat1-dev libssl-dev libgdbm-dev \
-    tk-dev libgc-dev python-cffi \
-    liblzma-dev libncursesw-dev      # these two only needed on PyPy3
-
 On Fedora::
 
     dnf install gcc make libffi-devel pkgconfig zlib-devel bzip2-devel \
     sqlite-devel ncurses-devel expat-devel openssl-devel tk-devel \
-    gdbm-devel python-cffi\
+    gdbm-devel python-cffi gc-devel\
     xz-devel  # For lzma on PyPy3.
 
 On SLES11::
diff --git a/pypy/doc/release-v7.3.0.rst b/pypy/doc/release-v7.3.0.rst
--- a/pypy/doc/release-v7.3.0.rst
+++ b/pypy/doc/release-v7.3.0.rst
@@ -18,6 +18,12 @@
 building third party packages for python, so this release changes the ABI tag
 for PyPy.
 
+Based on the great work done in `portable-pypy`_, the linux downloads we
+provide are now built on top of the `manylinux2010`_ CentOS6 docker image. 
+The tarballs include the needed shared objects to run on any platform that
+supports manylinux2010 wheels, which should include all supported versions of
+debian- and RedHat-based distributions (including Ubuntu, CentOS, and Fedora).
+
 The `CFFI`_ backend has been updated to version 1.13.1. We recommend using CFFI
 rather than c-extensions to interact with C.
 
@@ -57,6 +63,8 @@
 .. _`CFFI`: http://cffi.readthedocs.io
 .. _`cppyy`: https://cppyy.readthedocs.io
 .. _`available as wheels`: https://github.com/antocuni/pypy-wheels
+.. _`portable-pypy`: https://github.com/squeaky-pl/portable-pypy
+.. _`manylinux2010`: https://github.com/pypa/manylinux
 
 What is PyPy?
 =============
@@ -124,6 +132,7 @@
 * Check for overflow in ctypes array creation
 * Better support and report MSVC versions used to compile on windows
 * Allow any kind of buffer in socket.setsockopt(), like CPython (`issue 3114`_)
+* Fix importing a module with unicode in ``sys.path`` (`issue 3112`_)
 
 C-API (cpyext) and c-extensions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -166,6 +175,8 @@
   it.  (`issue 3096`_)
 * Remove incorrect clobbering of the ``locals`` after running ``exec()``
 * Adds encoding, decoding codepages on win32
+* Remove socket error attributes from ``_ssl`` (`issue 3119`_)
+* Add missing ``os.getgrouplist`` (part of `issue 2375`_)
 
 Python 3.6 C-API
 ~~~~~~~~~~~~~~~~
@@ -182,6 +193,7 @@
 .. _`manylinux2010`: fix broken link
 .. _`macports pypy`: https://github.com/macports/macports-ports/blob/master/lang/pypy/files/darwin.py.diff
 
+.. _`issue 2375`: https://bitbucket.com/pypy/pypy/issues/2375
 .. _`issue 2389`: https://bitbucket.com/pypy/pypy/issues/2389
 .. _`issue 2687`: https://bitbucket.com/pypy/pypy/issues/2687
 .. _`issue 2970`: https://bitbucket.com/pypy/pypy/issues/2970
@@ -198,8 +210,10 @@
 .. _`issue 3100`: https://bitbucket.com/pypy/pypy/issues/3100
 .. _`issue 3108`: https://bitbucket.com/pypy/pypy/issues/3108
 .. _`issue 3109`: https://bitbucket.com/pypy/pypy/issues/3109
+.. _`issue 3112`: https://bitbucket.com/pypy/pypy/issues/3112
 .. _`issue 3114`: https://bitbucket.com/pypy/pypy/issues/3114
 .. _`issue 3117`: https://bitbucket.com/pypy/pypy/issues/3117
+.. _`issue 3119`: https://bitbucket.com/pypy/pypy/issues/3119
 .. _`issue 3120`: https://bitbucket.com/pypy/pypy/issues/3120
 
 .. _13312: https://bugs.python.org/issue13312
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -5,4 +5,6 @@
 .. this is a revision shortly after release-pypy-7.3.0
 .. startrev: dbbbae99135f 
 
+.. branch: backport-decode_timeval_ns-py3.7
 
+Backport ``rtime.decode_timeval_ns`` from py3.7 to rpython
diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-head.rst
--- a/pypy/doc/whatsnew-pypy3-head.rst
+++ b/pypy/doc/whatsnew-pypy3-head.rst
@@ -5,15 +5,3 @@
 .. this is the revision after release-pypy3.6-v7.3.0
 .. startrev: 78b4d0a7cf2e
 
-
-.. branch: py3.6-asyncgen
-
-Fix asyncgen_hooks and refactor coroutine execution
-
-.. branch: py3.6-exc-info
-
-Follow CPython's use of exc_info more closely (issue 3096)
-
-.. branch: code_page-utf8
-
-Add encoding, decoding of codepages on windows
diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst
--- a/pypy/doc/windows.rst
+++ b/pypy/doc/windows.rst
@@ -155,7 +155,7 @@
 the `get_externals.py` utility to checkout the proper branch for your platform
 and PyPy version.
 
-.. _subrepository:  https://bitbucket.org/pypy/external
+.. _subrepository:  https://bitbucket.org/pypy/externals
 
 Using the mingw compiler
 ------------------------
diff --git a/pypy/interpreter/app_main.py b/pypy/interpreter/app_main.py
--- a/pypy/interpreter/app_main.py
+++ b/pypy/interpreter/app_main.py
@@ -734,7 +734,15 @@
             filename = sys.argv[0]
             mainmodule.__file__ = filename
             mainmodule.__cached__ = None
-            if not isolated:
+            for hook in sys.path_hooks:
+                try:
+                    importer = hook(filename)
+                    break
+                except ImportError:
+                    continue
+            else:
+                importer = None
+            if importer is None and not isolated:
                 sys.path.insert(0, sys.pypy_resolvedirof(filename))
             # assume it's a pyc file only if its name says so.
             # CPython goes to great lengths to detect other cases
@@ -770,18 +778,13 @@
                 args = (execfile, filename, mainmodule.__dict__)
             else:
                 filename = sys.argv[0]
-                for hook in sys.path_hooks:
-                    try:
-                        importer = hook(filename)
-                    except ImportError:
-                        continue
+                if importer is not None:
                     # It's the name of a directory or a zip file.
                     # put the filename in sys.path[0] and import
                     # the module __main__
                     import runpy
                     sys.path.insert(0, filename)
                     args = (runpy._run_module_as_main, '__main__', False)
-                    break
                 else:
                     # That's the normal path, "pypy3 stuff.py".
                     # We don't actually load via SourceFileLoader
diff --git a/pypy/interpreter/test/apptest_coroutine.py b/pypy/interpreter/test/apptest_coroutine.py
--- a/pypy/interpreter/test/apptest_coroutine.py
+++ b/pypy/interpreter/test/apptest_coroutine.py
@@ -699,6 +699,72 @@
 
     assert run_async(run()) == ([], (1,))
 
+# Helpers for test_async_gen_exception_11() below
+def sync_iterate(g):
+    res = []
+    while True:
+        try:
+            res.append(g.__next__())
+        except StopIteration:
+            res.append('STOP')
+            break
+        except Exception as ex:
+            res.append(str(type(ex)))
+    return res
+
+def async_iterate(g):
+    res = []
+    while True:
+        try:
+            g.__anext__().__next__()
+        except StopAsyncIteration:
+            res.append('STOP')
+            break
+        except StopIteration as ex:
+            if ex.args:
+                res.append(ex.args[0])
+            else:
+                res.append('EMPTY StopIteration')
+                break
+        except Exception as ex:
+            res.append(str(type(ex)))
+    return res
+
+
+def test_async_gen_exception_11():
+    # bpo-33786
+    def sync_gen():
+        yield 10
+        yield 20
+
+    def sync_gen_wrapper():
+        yield 1
+        sg = sync_gen()
+        sg.send(None)
+        try:
+            sg.throw(GeneratorExit())
+        except GeneratorExit:
+            yield 2
+        yield 3
+
+    async def async_gen():
+        yield 10
+        yield 20
+
+    async def async_gen_wrapper():
+        yield 1
+        asg = async_gen()
+        await asg.asend(None)
+        try:
+            await asg.athrow(GeneratorExit())
+        except GeneratorExit:
+            yield 2
+        yield 3
+
+    sync_gen_result = sync_iterate(sync_gen_wrapper())
+    async_gen_result = async_iterate(async_gen_wrapper())
+    assert sync_gen_result == async_gen_result
+
 def test_asyncgen_yield_stopiteration():
     async def foo():
         yield 1
diff --git a/pypy/interpreter/test/test_generator.py b/pypy/interpreter/test/apptest_generator.py
copy from pypy/interpreter/test/test_generator.py
copy to pypy/interpreter/test/apptest_generator.py
--- a/pypy/interpreter/test/test_generator.py
+++ b/pypy/interpreter/test/apptest_generator.py
@@ -1,989 +1,857 @@
-class AppTestGenerator:
+from pytest import raises, skip
 
-    def test_generator(self):
-        def f():
+def test_generator():
+    def f():
+        yield 1
+    assert next(f()) == 1
+
+def test_generator2():
+    def f():
+        yield 1
+    g = f()
+    assert next(g) == 1
+    with raises(StopIteration):
+        next(g)
+
+def test_attributes():
+    def f():
+        yield 1
+        assert g.gi_running
+    g = f()
+    assert g.gi_code is f.__code__
+    assert g.__name__ == 'f'
+    assert g.gi_frame is not None
+    assert not g.gi_running
+    next(g)
+    assert not g.gi_running
+    with raises(StopIteration):
+        next(g)
+    assert not g.gi_running
+    assert g.gi_frame is None
+    assert g.gi_code is f.__code__
+    assert g.__name__ == 'f'
+
+def test_generator3():
+    def f():
+        yield 1
+    g = f()
+    assert list(g) == [1]
+
+def test_generator4():
+    def f():
+        yield 1
+    g = f()
+    assert [x for x in g] == [1]
+
+def test_generator5():
+    def f():
+        v = (yield)
+        yield v
+    g = f()
+    next(g)
+    assert g.send(42) == 42
+
+def test_throw1():
+    def f():
+        yield 2
+    g = f()
+    # two arguments version
+    with raises(NameError):
+        g.throw(NameError, "Error")
+
+def test_throw2():
+    def f():
+        yield 2
+    g = f()
+    # single argument version
+    with raises(NameError):
+        g.throw(NameError("Error"))
+
+def test_throw3():
+    def f():
+        try:
             yield 1
-        assert next(f()) == 1
+            yield 2
+        except NameError:
+            yield 3
+    g = f()
+    assert next(g) == 1
+    assert g.throw(NameError("Error")) == 3
+    with raises(StopIteration):
+        next(g)
 
-    def test_generator2(self):
-        def f():
+def test_throw4():
+    def f():
+        try:
             yield 1
-        g = f()
-        assert next(g) == 1
-        raises(StopIteration, next, g)
+            v = (yield 2)
+        except NameError:
+            yield 3
+    g = f()
+    assert next(g) == 1
+    assert next(g) == 2
+    assert g.throw(NameError("Error")) == 3
+    with raises(StopIteration):
+        next(g)
 
-    def test_attributes(self):
-        def f():
+def test_throw5():
+    def f():
+        try:
             yield 1
-            assert g.gi_running
-        g = f()
-        assert g.gi_code is f.__code__
-        assert g.__name__ == 'f'
-        assert g.gi_frame is not None
-        assert not g.gi_running
+        except Exception:
+            x = 3
+        try:
+            yield x
+        except Exception:
+            pass
+    g = f()
+    next(g)
+    # String exceptions are not allowed anymore
+    with raises(TypeError):
+        g.throw("Error")
+    assert g.throw(Exception) == 3
+    with raises(StopIteration):
+        g.throw(Exception)
+
+def test_throw6():
+    def f():
+        yield 2
+    g = f()
+    with raises(NameError):
+        g.throw(NameError, "Error", None)
+
+
+def test_throw_fail():
+    def f():
+        yield 1
+    g = f()
+    with raises(TypeError):
+        g.throw(NameError("Error"), "error")
+
+def test_throw_fail2():
+    def f():
+        yield 1
+    g = f()
+    with raises(TypeError):
+        g.throw(list())
+
+def test_throw_fail3():
+    def f():
+        yield 1
+    g = f()
+    with raises(TypeError):
+        g.throw(NameError("Error"), None, "not tb object")
+
+def test_throw_finishes_generator():
+    def f():
+        yield 1
+    g = f()
+    assert g.gi_frame is not None
+    with raises(ValueError):
+        g.throw(ValueError)
+    assert g.gi_frame is None
+
+def test_throw_bug():
+    def f():
+        try:
+            x.throw(IndexError)     # => "generator already executing"
+        except ValueError:
+            yield 1
+    x = f()
+    res = list(x)
+    assert res == [1]
+
+def test_throw_on_finished_generator():
+    def f():
+        yield 1
+    g = f()
+    res = next(g)
+    assert res == 1
+    with raises(StopIteration):
         next(g)
-        assert not g.gi_running
-        raises(StopIteration, next, g)
-        assert not g.gi_running
-        assert g.gi_frame is None
-        assert g.gi_code is f.__code__
-        assert g.__name__ == 'f'
+    with raises(NameError):
+        g.throw(NameError)
 
-    def test_generator3(self):
-        def f():
+def test_throw_tb():
+    def f():
+        try:
+            yield
+        except ZeroDivisionError:
+            raise
+    g = f()
+    try:
+        1 / 0
+    except ZeroDivisionError as v:
+        try:
+            g.throw(v)
+        except Exception as w:
+            tb = w.__traceback__
+    levels = 0
+    while tb:
+        levels += 1
+        tb = tb.tb_next
+    assert levels == 3
+
+def test_throw_context():
+    # gen.throw(exc) must not modify exc.__context__
+    def gen():
+        try:
+            yield
+        except Exception:
+            raise ValueError
+
+    try:
+        raise KeyError
+    except KeyError:
+        g = gen()
+        next(g)
+        exc1 = Exception(1)
+        exc2 = Exception(2)
+        exc2.__context__ = exc1
+        try:
+            g.throw(exc2)
+        except ValueError:
+            assert exc2.__context__ is exc1
+
+def test_close():
+    def f():
+        yield 1
+    g = f()
+    assert g.close() is None
+
+def test_close2():
+    def f():
+        try:
             yield 1
-        g = f()
-        assert list(g) == [1]
+        except GeneratorExit:
+            raise StopIteration
+    g = f()
+    next(g)
+    assert g.close() is None
 
-    def test_generator4(self):
-        def f():
+def test_close3():
+    def f():
+        try:
             yield 1
-        g = f()
-        assert [x for x in g] == [1]
+        except GeneratorExit:
+            raise NameError
+    g = f()
+    next(g)
+    with raises(NameError):
+        g.close()
 
-    def test_generator5(self):
-        d = {}
-        exec("""if 1:
-        def f():
-            v = (yield )
-            yield v
-        g = f()
+def test_close_fail():
+    def f():
+        try:
+            yield 1
+        except GeneratorExit:
+            yield 2
+    g = f()
+    next(g)
+    with raises(RuntimeError):
+        g.close()
+
+def test_close_on_collect():
+    import gc
+    def f():
+        try:
+            yield
+        finally:
+            f.x = 42
+    g = f()
+    next(g)
+    del g
+    gc.collect()
+    assert f.x == 42
+
+def test_generator_raises_typeerror():
+    def f():
+        yield 1
+    g = f()
+    with raises(TypeError):
+        g.send()     # one argument required
+    with raises(TypeError):
+        g.send(1)  # not started, must send None
+
+def test_generator_explicit_stopiteration():
+    def f():
+        yield 1
+        raise StopIteration
+    g = f()
+    assert [x for x in g] == [1]
+
+def test_generator_propagate_stopiteration():
+    def f():
+        it = iter([1])
+        while 1:
+            yield next(it)
+    g = f()
+    assert [x for x in g] == [1]
+
+def test_generator_restart():
+    def g():
+        i = next(me)
+        yield i
+    me = g()
+    with raises(ValueError):
+        next(me)
+
+def test_generator_expression():
+    d = {}
+    exec("res = sum(i*i for i in range(5))", d, d)
+    assert d['res'] == 30
+
+def test_generator_expression_2():
+    def f():
+        total = sum(i for i in [x for x in z])
+        return total
+    z = [1, 2, 7]
+    assert f() == 10
+
+def test_repr():
+    def myFunc():
+        yield 1
+    g = myFunc()
+    r = repr(g)
+    assert r.startswith("<generator object test_repr.<locals>.myFunc at 0x")
+    assert list(g) == [1]
+    assert repr(g) == r
+
+def test_unpackiterable_gen():
+    g = (i * i for i in range(-5, 3))
+    assert set(g) == set([0, 1, 4, 9, 16, 25])
+    assert set(g) == set()
+    assert set(i for i in range(0)) == set()
+
+def test_explicit_stop_iteration_unpackiterable():
+    def f():
+        yield 1
+        raise StopIteration
+    assert tuple(f()) == (1,)
+
+def test_exception_is_cleared_by_yield():
+    def f():
+        try:
+            foobar
+        except NameError:
+            yield 5
+            raise
+    gen = f()
+    next(gen)  # --> 5
+    try:
+        next(gen)
+    except NameError:
+        pass
+
+def test_yield_return():
+    def f():
+        yield 1
+        return 2
+    g = f()
+    assert next(g) == 1
+    try:
         next(g)
-        """, d, d)
-        g = d['g']
-        assert g.send(42) == 42
+    except StopIteration as e:
+        assert e.value == 2
+    else:
+        assert False, 'Expected StopIteration'
 
-    def test_throw1(self):
-        def f():
-            yield 2
-        g = f()
-        # two arguments version
-        raises(NameError, g.throw, NameError, "Error")
+def test_yield_from_basic():
+    def f1():
+        yield from []
+        yield from [1, 2, 3]
+        yield from f2()
+    def f2():
+        yield 4
+        yield 5
+    gen = f1()
+    assert next(gen) == 1
+    assert next(gen) == 2
+    assert next(gen) == 3
+    assert next(gen) == 4
+    assert next(gen) == 5
+    assert list(gen) == []
 
-    def test_throw2(self):
-        def f():
-            yield 2
-        g = f()
-        # single argument version
-        raises(NameError, g.throw, NameError("Error"))
+def test_yield_from_return():
+    def f1():
+        result = yield from f2()
+        return result
+    def f2():
+        yield 1
+        return 2
+    g = f1()
+    assert next(g) == 1
+    try:
+        next(g)
+    except StopIteration as e:
+        assert e.value == 2
+    else:
+        assert False, 'Expected StopIteration'
 
-    def test_throw3(self):
-        def f():
-            try:
-                yield 1
-                yield 2
-            except:
-                yield 3
-        g = f()
-        assert next(g) == 1
-        assert g.throw(NameError("Error")) == 3
-        raises(StopIteration, next, g)
+def test_yield_from_return_tuple():
+    def f1():
+        result = yield from f2()
+        return result
+    def f2():
+        yield 1
+        return (1, 2)
+    g = f1()
+    assert next(g) == 1
+    try:
+        next(g)
+    except StopIteration as e:
+        assert e.value == (1, 2)
+    else:
+        assert False, 'Expected StopIteration'
 
-    def test_throw4(self):
-        d = {}
-        exec("""if 1:
-        def f():
-            try:
-                yield 1
-                v = (yield 2)
-            except:
-                yield 3
-        g = f()
-        """, d, d)
-        g = d['g']
-        assert next(g) == 1
-        assert next(g) == 2
-        assert g.throw(NameError("Error")) == 3
-        raises(StopIteration, next, g)
+def test_set_name_qualname():
+    class A:
+        def f(self):
+            yield 5
+    g = A().f()
+    assert g.__name__ == "f"
+    assert g.__qualname__ == "test_set_name_qualname.<locals>.A.f"
+    g.__name__ = "h.i"
+    g.__qualname__ = "j.k"
+    assert g.__name__ == "h.i"
+    assert g.__qualname__ == "j.k"
+    with raises(TypeError):
+        g.__name__ = 42
+    with raises(TypeError):
+        g.__qualname__ = 42
+    with raises((TypeError, AttributeError)):
+        del g.__name__
+    with raises((TypeError, AttributeError)):
+        del g.__qualname__
 
-    def test_throw5(self):
-        def f():
-            try:
-                yield 1
-            except:
-                x = 3
-            try:
-                yield x
-            except:
-                pass
-        g = f()
-        next(g)
-        # String exceptions are not allowed anymore
-        raises(TypeError, g.throw, "Error")
-        assert g.throw(Exception) == 3
-        raises(StopIteration, g.throw, Exception)
+def test_gi_yieldfrom():
+    def g(x):
+        assert gen.gi_yieldfrom is None
+        yield x
+        assert gen.gi_yieldfrom is None
+    def f(x):
+        assert gen.gi_yieldfrom is None
+        yield from g(x)
+        assert gen.gi_yieldfrom is None
+        yield 42
+        assert gen.gi_yieldfrom is None
+    gen = f(5)
+    assert gen.gi_yieldfrom is None
+    assert next(gen) == 5
+    assert gen.gi_yieldfrom.__name__ == 'g'
+    assert next(gen) == 42
+    assert gen.gi_yieldfrom is None
 
-    def test_throw6(self):
-        def f():
-            yield 2
-        g = f()
-        raises(NameError, g.throw, NameError, "Error", None)
+def test_gi_running_in_throw_generatorexit():
+    # We must force gi_running to be True on the outer generators
+    # when running an inner custom close() method.
+    class A:
+        def __iter__(self):
+            return self
+        def __next__(self):
+            return 42
+        def close(self):
+            closed.append(gen.gi_running)
+    def g():
+        yield from A()
+    gen = g()
+    assert next(gen) == 42
+    closed = []
+    with raises(GeneratorExit):
+        gen.throw(GeneratorExit)
+    assert closed == [True]
 
-
-    def test_throw_fail(self):
-        def f():
-            yield 1
-        g = f()
-        raises(TypeError, g.throw, NameError("Error"), "error")
-
-    def test_throw_fail2(self):
-        def f():
-            yield 1
-        g = f()
-        raises(TypeError, g.throw, list())
-
-    def test_throw_fail3(self):
-        def f():
-            yield 1
-        g = f()
-        raises(TypeError, g.throw, NameError("Error"), None, "not tb object")
-
-    def test_throw_finishes_generator(self):
-        def f():
-            yield 1
-        g = f()
-        assert g.gi_frame is not None
-        raises(ValueError, g.throw, ValueError)
-        assert g.gi_frame is None
-
-    def test_throw_bug(self):
-        def f():
-            try:
-                x.throw(IndexError)     # => "generator already executing"
-            except ValueError:
-                yield 1
-        x = f()
-        res = list(x)
-        assert res == [1]
-
-    def test_throw_on_finished_generator(self):
-        def f():
-            yield 1
-        g = f()
-        res = next(g)
-        assert res == 1
-        raises(StopIteration, next, g)
-        raises(NameError, g.throw, NameError)
-
-    def test_throw_tb(self):
-        def f():
-            try:
-                yield
-            except:
-                raise
-        g = f()
-        try:
-            1/0
-        except ZeroDivisionError as v:
-            try:
-                g.throw(v)
-            except Exception as w:
-                tb = w.__traceback__
-        levels = 0
-        while tb:
-            levels += 1
-            tb = tb.tb_next
-        assert levels == 3
-
-    def test_throw_context(self):
-        # gen.throw(exc) must not modify exc.__context__
-        def gen():
-            try:
-                yield
-            except:
-                raise ValueError
-
-        try:
-            raise KeyError
-        except KeyError:
-            g = gen()
-            next(g)
-            exc1 = Exception(1)
-            exc2 = Exception(2)
-            exc2.__context__ = exc1
-            try:
-                g.throw(exc2)
-            except ValueError:
-                assert exc2.__context__ is exc1
-
-    def test_close(self):
-        def f():
-            yield 1
-        g = f()
-        assert g.close() is None
-
-    def test_close2(self):
-        def f():
-            try:
-                yield 1
-            except GeneratorExit:
-                raise StopIteration
-        g = f()
-        next(g)
-        assert g.close() is None
-
-    def test_close3(self):
-        def f():
-            try:
-                yield 1
-            except GeneratorExit:
-                raise NameError
-        g = f()
-        next(g)
-        raises(NameError, g.close)
-
-    def test_close_fail(self):
-        def f():
-            try:
-                yield 1
-            except GeneratorExit:
-                yield 2
-        g = f()
-        next(g)
-        raises(RuntimeError, g.close)
-
-    def test_close_on_collect(self):
-        def f():
-            try:
-                yield
-            finally:
-                f.x = 42
-        g = f()
-        next(g)
-        del g
-        import gc
-        gc.collect()
-        assert f.x == 42
-
-    def test_generator_raises_typeerror(self):
-        def f():
-            yield 1
-        g = f()
-        raises(TypeError, g.send)     # one argument required
-        raises(TypeError, g.send, 1)  # not started, must send None
-
-    def test_generator_explicit_stopiteration(self):
-        def f():
-            yield 1
-            raise StopIteration
-        g = f()
-        assert [x for x in g] == [1]
-
-    def test_generator_propagate_stopiteration(self):
-        def f():
-            it = iter([1])
-            while 1: yield next(it)
-        g = f()
-        assert [x for x in g] == [1]
-
-    def test_generator_restart(self):
-        def g():
-            i = next(me)
-            yield i
-        me = g()
-        raises(ValueError, next, me)
-
-    def test_generator_expression(self):
-        d = {}
-        exec("res = sum(i*i for i in range(5))", d, d)
-        assert d['res'] == 30
-
-    def test_generator_expression_2(self):
-        d = {}
-        exec("""
-def f():
-    total = sum(i for i in [x for x in z])
-    return total
-z = [1, 2, 7]
-res = f()
-""", d, d)
-        assert d['res'] == 10
-
-    def test_repr(self):
-        def myFunc():
-            yield 1
-        g = myFunc()
-        r = repr(g)
-        assert r.startswith("<generator object test_repr.<locals>.myFunc at 0x")
-        assert list(g) == [1]
-        assert repr(g) == r
-
-    def test_unpackiterable_gen(self):
-        g = (i*i for i in range(-5, 3))
-        assert set(g) == set([0, 1, 4, 9, 16, 25])
-        assert set(g) == set()
-        assert set(i for i in range(0)) == set()
-
-    def test_explicit_stop_iteration_unpackiterable(self):
-        def f():
-            yield 1
-            raise StopIteration
-        assert tuple(f()) == (1,)
-
-    def test_exception_is_cleared_by_yield(self):
-        def f():
-            try:
-                foobar
-            except NameError:
-                yield 5
-                raise
-        gen = f()
-        next(gen)  # --> 5
-        try:
-            next(gen)
-        except NameError:
-            pass
-
-    def test_yield_return(self):
-        """
-        def f():
-            yield 1
-            return 2
-        g = f()
-        assert next(g) == 1
-        try:
-            next(g)
-        except StopIteration as e:
-            assert e.value == 2
-        else:
-            assert False, 'Expected StopIteration'
-            """
-
-    def test_yield_from_basic(self):
-        """
-        def f1():
-            yield from []
-            yield from [1, 2, 3]
-            yield from f2()
-        def f2():
-            yield 4
-            yield 5
-        gen = f1()
-        assert next(gen) == 1
-        assert next(gen) == 2
-        assert next(gen) == 3
-        assert next(gen) == 4
-        assert next(gen) == 5
-        assert list(gen) == []
-        """
-
-    def test_yield_from_return(self):
-        """
-        def f1():
-            result = yield from f2()
-            return result
-        def f2():
-            yield 1
-            return 2
-        g = f1()
-        assert next(g) == 1
-        try:
-            next(g)
-        except StopIteration as e:
-            assert e.value == 2
-        else:
-            assert False, 'Expected StopIteration'
-            """
-
-    def test_yield_from_return_tuple(self):
-        """
-        def f1():
-            result = yield from f2()
-            return result
-        def f2():
-            yield 1
-            return (1, 2)
-        g = f1()
-        assert next(g) == 1
-        try:
-            next(g)
-        except StopIteration as e:
-            assert e.value == (1, 2)
-        else:
-            assert False, 'Expected StopIteration'
-            """
-
-    def test_set_name_qualname(self):
-        class A:
-            def f(self):
-                yield 5
-        g = A().f()
-        assert g.__name__ == "f"
-        assert g.__qualname__ == "test_set_name_qualname.<locals>.A.f"
-        g.__name__ = "h.i"
-        g.__qualname__ = "j.k"
-        assert g.__name__ == "h.i"
-        assert g.__qualname__ == "j.k"
-        raises(TypeError, "g.__name__ = 42")
-        raises(TypeError, "g.__qualname__ = 42")
-        raises((TypeError, AttributeError), "del g.__name__")
-        raises((TypeError, AttributeError), "del g.__qualname__")
-
-    def test_gi_yieldfrom(self): """
-        def g(x):
-            assert gen.gi_yieldfrom is None
-            yield x
-            assert gen.gi_yieldfrom is None
-        def f(x):
-            assert gen.gi_yieldfrom is None
-            yield from g(x)
-            assert gen.gi_yieldfrom is None
-            yield 42
-            assert gen.gi_yieldfrom is None
-        gen = f(5)
-        assert gen.gi_yieldfrom is None
-        assert next(gen) == 5
-        assert gen.gi_yieldfrom.__name__ == 'g'
-        assert next(gen) == 42
-        assert gen.gi_yieldfrom is None
-        """
-
-    def test_gi_running_in_throw_generatorexit(self): """
-        # We must force gi_running to be True on the outer generators
-        # when running an inner custom close() method.
-        class A:
-            def __iter__(self):
-                return self
-            def __next__(self):
-                return 42
-            def close(self):
-                closed.append(gen.gi_running)
-        def g():
-            yield from A()
-        gen = g()
-        assert next(gen) == 42
-        closed = []
-        raises(GeneratorExit, gen.throw, GeneratorExit)
-        assert closed == [True]
-        """
-
-    def test_exc_info_in_generator(self):
-        import sys
-        def g():
-            try:
-                raise ValueError
-            except ValueError:
-                yield sys.exc_info()[0]
-                yield sys.exc_info()[0]
-        try:
-            raise IndexError
-        except IndexError:
-            gen = g()
-            assert sys.exc_info()[0] is IndexError
-            assert next(gen) is ValueError
-            assert sys.exc_info()[0] is IndexError
-            assert next(gen) is ValueError
-            assert sys.exc_info()[0] is IndexError
-            raises(StopIteration, next, gen)
-            assert sys.exc_info()[0] is IndexError
-
-    def test_exc_info_in_generator_2(self):
-        import sys
-        def g():
-            yield sys.exc_info()[0]
-            try:
-                raise LookupError
-            except LookupError:
-                yield sys.exc_info()[0]
-            yield sys.exc_info()[0]
-        try:
-            raise IndexError
-        except IndexError:
-            gen = g()     # the IndexError is not captured at all
+def test_exc_info_in_generator():
+    import sys
+    def g():
         try:
             raise ValueError
         except ValueError:
-            assert next(gen) is ValueError
-            assert next(gen) is LookupError
-            assert next(gen) is ValueError
+            yield sys.exc_info()[0]
+            yield sys.exc_info()[0]
+    try:
+        raise IndexError
+    except IndexError:
+        gen = g()
+        assert sys.exc_info()[0] is IndexError
+        assert next(gen) is ValueError
+        assert sys.exc_info()[0] is IndexError
+        assert next(gen) is ValueError
+        assert sys.exc_info()[0] is IndexError
+        with raises(StopIteration):
+            next(gen)
+        assert sys.exc_info()[0] is IndexError
 
-    def test_exc_info_in_generator_3(self):
-        import sys
-        def g():
+def test_exc_info_in_generator_2():
+    import sys
+    def g():
+        yield sys.exc_info()[0]
+        try:
+            raise LookupError
+        except LookupError:
             yield sys.exc_info()[0]
-            yield sys.exc_info()[0]
-            yield sys.exc_info()[0]
-        gen = g()
-        try:
-            raise IndexError
-        except IndexError:
-            assert next(gen) is IndexError
-        assert next(gen) is None
+        yield sys.exc_info()[0]
+    try:
+        raise IndexError
+    except IndexError:
+        gen = g()     # the IndexError is not captured at all
+    try:
+        raise ValueError
+    except ValueError:
+        assert next(gen) is ValueError
+        assert next(gen) is LookupError
+        assert next(gen) is ValueError
+
+def test_exc_info_in_generator_3():
+    import sys
+    def g():
+        yield sys.exc_info()[0]
+        yield sys.exc_info()[0]
+        yield sys.exc_info()[0]
+    gen = g()
+    try:
+        raise IndexError
+    except IndexError:
+        assert next(gen) is IndexError
+    assert next(gen) is None
+    try:
+        raise ValueError
+    except ValueError:
+        assert next(gen) is ValueError
+
+def test_exc_info_in_generator_4():
+    skip("buggy behavior, both in CPython and in PyPy")
+    import sys
+    def g():
         try:
             raise ValueError
         except ValueError:
-            assert next(gen) is ValueError
+            yield 1
+        assert sys.exc_info() == (None, None, None)
+        yield 2
+    gen = g()
+    try:
+        raise IndexError
+    except IndexError:
+        assert next(gen) is 1
+    assert next(gen) is 2
 
-    def test_exc_info_in_generator_4(self):
-        skip("buggy behavior, both in CPython and in PyPy")
-        import sys
-        def g():
-            try:
-                raise ValueError
-            except ValueError:
-                yield 1
-            assert sys.exc_info() == (None, None, None)
-            yield 2
-        gen = g()
+def test_multiple_invalid_sends():
+    def mygen():
+        yield 42
+    g = mygen()
+    with raises(TypeError):
+        g.send(2)
+    with raises(TypeError):
+        g.send(2)
+
+def test_delegating_close():
+    """
+    Test delegating 'close'
+    """
+    trace = []
+    def g1():
         try:
-            raise IndexError
-        except IndexError:
-            assert next(gen) is 1
-        assert next(gen) is 2
+            trace.append("Starting g1")
+            yield "g1 ham"
+            yield from g2()
+            yield "g1 eggs"
+        finally:
+            trace.append("Finishing g1")
+    def g2():
+        try:
+            trace.append("Starting g2")
+            yield "g2 spam"
+            yield "g2 more spam"
+        finally:
+            trace.append("Finishing g2")
+    g = g1()
+    for i in range(2):
+        x = next(g)
+        trace.append("Yielded %s" % (x,))
+    g.close()
+    assert trace == [
+        "Starting g1",
+        "Yielded g1 ham",
+        "Starting g2",
+        "Yielded g2 spam",
+        "Finishing g2",
+        "Finishing g1"
+    ]
 
-    def test_multiple_invalid_sends(self):
-        def mygen():
-            yield 42
-        g = mygen()
-        raises(TypeError, g.send, 2)
-        raises(TypeError, g.send, 2)
+def test_handing_exception_while_delegating_close():
+    """
+    Test handling exception while delegating 'close'
+    """
+    trace = []
+    def g1():
+        try:
+            trace.append("Starting g1")
+            yield "g1 ham"
+            yield from g2()
+            yield "g1 eggs"
+        finally:
+            trace.append("Finishing g1")
+    def g2():
+        try:
+            trace.append("Starting g2")
+            yield "g2 spam"
+            yield "g2 more spam"
+        finally:
+            trace.append("Finishing g2")
+            raise ValueError("nybbles have exploded with delight")
+    g = g1()
+    for i in range(2):
+        x = next(g)
+        trace.append("Yielded %s" % (x,))
+    with raises(ValueError) as excinfo:
+        g.close()
+    assert excinfo.value.args[0] == "nybbles have exploded with delight"
+    assert isinstance(excinfo.value.__context__, GeneratorExit)
+    assert trace == [
+        "Starting g1",
+        "Yielded g1 ham",
+        "Starting g2",
+        "Yielded g2 spam",
+        "Finishing g2",
+        "Finishing g1",
+    ]
 
+def test_delegating_throw():
+    """
+    Test delegating 'throw'
+    """
+    trace = []
+    def g1():
+        try:
+            trace.append("Starting g1")
+            yield "g1 ham"
+            yield from g2()
+            yield "g1 eggs"
+        finally:
+            trace.append("Finishing g1")
+    def g2():
+        try:
+            trace.append("Starting g2")
+            yield "g2 spam"
+            yield "g2 more spam"
+        finally:
+            trace.append("Finishing g2")
+    g = g1()
+    for i in range(2):
+        x = next(g)
+        trace.append("Yielded %s" % (x,))
+    e = ValueError("tomato ejected")
+    with raises(ValueError) as excinfo:
+        g.throw(e)
+    assert excinfo.value.args[0] == "tomato ejected"
+    assert trace == [
+        "Starting g1",
+        "Yielded g1 ham",
+        "Starting g2",
+        "Yielded g2 spam",
+        "Finishing g2",
+        "Finishing g1",
+    ]
 
-class AppTestAsyncGenerator(object):
+def test_delegating_throw_to_non_generator():
+    """
+    Test delegating 'throw' to non-generator
+    """
+    trace = []
+    def g():
+        try:
+            trace.append("Starting g")
+            yield from range(10)
+        finally:
+            trace.append("Finishing g")
+    gi = g()
+    for i in range(5):
+        x = next(gi)
+        trace.append("Yielded %s" % (x,))
+    with raises(ValueError) as excinfo:
+        gi.throw(ValueError("tomato ejected"))
+    assert excinfo.value.args[0] == "tomato ejected"
+    assert trace == [
+        "Starting g",
+        "Yielded 0",
+        "Yielded 1",
+        "Yielded 2",
+        "Yielded 3",
+        "Yielded 4",
+        "Finishing g",
+    ]
 
-    def test_async_gen_exception_11(self):
-        """
-        # bpo-33786
-        def compare_generators(sync_gen, async_gen):
-            def sync_iterate(g):
-                res = []
-                while True:
-                    try:
-                        res.append(g.__next__())
-                    except StopIteration:
-                        res.append('STOP')
-                        break
-                    except Exception as ex:
-                        res.append(str(type(ex)))
-                return res
+def test_broken_getattr_handling():
+    """
+    Test subiterator with a broken getattr implementation
+    """
+    import _io, sys
+    class Broken:
+        def __iter__(self):
+            return self
+        def __next__(self):
+            return 1
+        def __getattr__(self, attr):
+            1 / 0
 
-            def async_iterate(g):
-                res = []
-                while True:
-                    an = g.__anext__()
-                    try:
-                        while True:
-                            try:
-                                an.__next__()
-                            except StopIteration as ex:
-                                if ex.args:
-                                    res.append(ex.args[0])
-                                    break
-                                else:
-                                    res.append('EMPTY StopIteration')
-                                    break
-                            except StopAsyncIteration:
-                                raise
-                            except Exception as ex:
-                                res.append(str(type(ex)))
-                                break
-                    except StopAsyncIteration:
-                        res.append('STOP')
-                        break
-                return res
+    def g():
+        yield from Broken()
 
-            def async_iterate(g):
-                res = []
-                while True:
-                    try:
-                        g.__anext__().__next__()
-                    except StopAsyncIteration:
-                        res.append('STOP')
-                        break
-                    except StopIteration as ex:
-                        if ex.args:
-                            res.append(ex.args[0])
-                        else:
-                            res.append('EMPTY StopIteration')
-                            break
-                    except Exception as ex:
-                        res.append(str(type(ex)))
-                return res
+    gi = g()
+    assert next(gi) == 1
+    with raises(ZeroDivisionError):
+        gi.send(1)
 
-            sync_gen_result = sync_iterate(sync_gen)
-            async_gen_result = async_iterate(async_gen)
-            assert sync_gen_result == async_gen_result, "%s != %s" % (str(sync_gen_result), str(async_gen_result))
-            return async_gen_result
+    gi = g()
+    assert next(gi) == 1
+    with raises(ZeroDivisionError):
+        gi.throw(RuntimeError)
 
-        def sync_gen():
-            yield 10
-            yield 20
+    gi = g()
+    assert next(gi) == 1
+    sys.stderr = _io.StringIO()
+    gi.close()
+    assert 'ZeroDivisionError' in sys.stderr.getvalue()
 
-        def sync_gen_wrapper():
+def test_returning_value_from_delegated_throw():
+    """
+    Test returning value from delegated 'throw'
+    """
+    trace = []
+    class LunchError(Exception):
+        pass
+    def g1():
+        try:
+            trace.append("Starting g1")
+            yield "g1 ham"
+            yield from g2()
+            yield "g1 eggs"
+        finally:
+            trace.append("Finishing g1")
+    def g2():
+        try:
+            trace.append("Starting g2")
+            yield "g2 spam"
+            yield "g2 more spam"
+        except LunchError:
+            trace.append("Caught LunchError in g2")
+            yield "g2 lunch saved"
+            yield "g2 yet more spam"
+    g = g1()
+    for i in range(2):
+        x = next(g)
+        trace.append("Yielded %s" % (x,))
+    e = LunchError("tomato ejected")
+    g.throw(e)
+    for x in g:
+        trace.append("Yielded %s" % (x,))
+    assert trace == [
+        "Starting g1",
+        "Yielded g1 ham",
+        "Starting g2",
+        "Yielded g2 spam",
+        "Caught LunchError in g2",
+        "Yielded g2 yet more spam",
+        "Yielded g1 eggs",
+        "Finishing g1",
+    ]
+
+def test_catching_exception_from_subgen_and_returning():
+    """
+    Test catching an exception thrown into a
+    subgenerator and returning a value
+    """
+    trace = []
+    def inner():
+        try:
             yield 1
-            sg = sync_gen()
-            sg.send(None)
-            try:
-                sg.throw(GeneratorExit())
-            except GeneratorExit:
-                yield 2
-            yield 3
+        except ValueError:
+            trace.append("inner caught ValueError")
+        return 2
 
-        async def async_gen():
-            yield 10
-            yield 20
+    def outer():
+        v = yield from inner()
+        trace.append("inner returned %r to outer" % v)
+        yield v
+    g = outer()
+    trace.append(next(g))
+    trace.append(g.throw(ValueError))
+    assert trace == [
+        1,
+        "inner caught ValueError",
+        "inner returned 2 to outer",
+        2,
+    ]
 
-        async def async_gen_wrapper():
-            yield 1
-            asg = async_gen()
-            await asg.asend(None)
-            try:
-                await asg.athrow(GeneratorExit())
-            except GeneratorExit:
-                yield 2
-            yield 3
+def test_exception_context():
+    import operator
+    def f():
+        try:
+            raise ValueError
+        except ValueError:
+            yield from map(operator.truediv, [2, 3], [4, 0])
+    gen = f()
+    assert next(gen) == 0.5
+    try:
+        next(gen)
+    except ZeroDivisionError as e:
+        assert e.__context__ is not None
+        assert isinstance(e.__context__, ValueError)
+    else:
+        assert False, "should have raised"
 
-        compare_generators(sync_gen_wrapper(), async_gen_wrapper())
-        """
 
+def test_past_generator_stop():
+    # how it works without 'from __future__' import generator_stop
+    def f(x):
+        raise StopIteration
+        yield x
+    with raises(StopIteration):
+        next(f(5))
 
-def test_should_not_inline(space):
-    from pypy.interpreter.generator import should_not_inline
-    w_co = space.appexec([], '''():
-        def g(x):
-            yield x + 5
-        return g.__code__
-    ''')
-    assert should_not_inline(w_co) == False
-    w_co = space.appexec([], '''():
-        def g(x):
-            yield x + 5
-            yield x + 6
-        return g.__code__
-    ''')
-    assert should_not_inline(w_co) == True
-
-class AppTestYieldFrom:
-    def test_delegating_close(self):
-        """
-        Test delegating 'close'
-        """
-        trace = []
-        d = dict(trace=trace)
-        exec('''if 1:
-        def g1():
-            try:
-                trace.append("Starting g1")
-                yield "g1 ham"
-                yield from g2()
-                yield "g1 eggs"
-            finally:
-                trace.append("Finishing g1")
-        def g2():
-            try:
-                trace.append("Starting g2")
-                yield "g2 spam"
-                yield "g2 more spam"
-            finally:
-                trace.append("Finishing g2")
-        ''', d)
-        g1, g2 = d['g1'], d['g2']
-        g = g1()
-        for i in range(2):
-            x = next(g)
-            trace.append("Yielded %s" % (x,))
-        g.close()
-        assert trace == [
-            "Starting g1",
-            "Yielded g1 ham",
-            "Starting g2",
-            "Yielded g2 spam",
-            "Finishing g2",
-            "Finishing g1"
-        ]
-
-    def test_handing_exception_while_delegating_close(self):
-        """
-        Test handling exception while delegating 'close'
-        """
-        trace = []
-        d = dict(trace=trace)
-        exec('''if 1:
-        def g1():
-            try:
-                trace.append("Starting g1")
-                yield "g1 ham"
-                yield from g2()
-                yield "g1 eggs"
-            finally:
-                trace.append("Finishing g1")
-        def g2():
-            try:
-                trace.append("Starting g2")
-                yield "g2 spam"
-                yield "g2 more spam"
-            finally:
-                trace.append("Finishing g2")
-                raise ValueError("nybbles have exploded with delight")
-        ''', d)
-        g1, g2 = d['g1'], d['g2']
-        g = g1()
-        for i in range(2):
-            x = next(g)
-            trace.append("Yielded %s" % (x,))
-        exc = raises(ValueError, g.close)
-        assert exc.value.args[0] == "nybbles have exploded with delight"
-        assert isinstance(exc.value.__context__, GeneratorExit)
-        assert trace == [
-            "Starting g1",
-            "Yielded g1 ham",
-            "Starting g2",
-            "Yielded g2 spam",
-            "Finishing g2",
-            "Finishing g1",
-        ]
-
-    def test_delegating_throw(self):
-        """
-        Test delegating 'throw'
-        """
-        trace = []
-        d = dict(trace=trace)
-        exec('''if 1:
-        def g1():
-            try:
-                trace.append("Starting g1")
-                yield "g1 ham"
-                yield from g2()
-                yield "g1 eggs"
-            finally:
-                trace.append("Finishing g1")
-        def g2():
-            try:
-                trace.append("Starting g2")
-                yield "g2 spam"
-                yield "g2 more spam"
-            finally:
-                trace.append("Finishing g2")
-        ''', d)
-        g1, g2 = d['g1'], d['g2']
-        g = g1()
-        for i in range(2):
-            x = next(g)
-            trace.append("Yielded %s" % (x,))
-        e = ValueError("tomato ejected")
-        exc = raises(ValueError, g.throw, e)
-        assert exc.value.args[0] == "tomato ejected"
-        assert trace == [
-            "Starting g1",
-            "Yielded g1 ham",
-            "Starting g2",
-            "Yielded g2 spam",
-            "Finishing g2",
-            "Finishing g1",
-        ]
-
-    def test_delegating_throw_to_non_generator(self):
-        """
-        Test delegating 'throw' to non-generator
-        """
-        trace = []
-        d = dict(trace=trace)
-        exec('''if 1:
-        def g():
-            try:
-                trace.append("Starting g")
-                yield from range(10)
-            finally:
-                trace.append("Finishing g")
-        ''', d)
-        g = d['g']
-        gi = g()
-        for i in range(5):
-            x = next(gi)
-            trace.append("Yielded %s" % (x,))
-        exc = raises(ValueError, gi.throw, ValueError("tomato ejected"))
-        assert exc.value.args[0] == "tomato ejected"
-        assert trace == [
-            "Starting g",
-            "Yielded 0",
-            "Yielded 1",
-            "Yielded 2",
-            "Yielded 3",
-            "Yielded 4",
-            "Finishing g",
-        ]
-
-    def test_broken_getattr_handling(self):
-        """
-        Test subiterator with a broken getattr implementation
-        """
-        class Broken:
-            def __iter__(self):
-                return self
-            def __next__(self):
-                return 1
-            def __getattr__(self, attr):
-                1/0
-
-        d = dict(Broken=Broken)
-        exec('''if 1:
-        def g():
-            yield from Broken()
-        ''', d)
-        g = d['g']
-
-        gi = g()
-        assert next(gi) == 1
-        raises(ZeroDivisionError, gi.send, 1)
-
-        gi = g()
-        assert next(gi) == 1
-        raises(ZeroDivisionError, gi.throw, RuntimeError)
-
-        gi = g()
-        assert next(gi) == 1
-        import io, sys
-        sys.stderr = io.StringIO()
-        gi.close()
-        assert 'ZeroDivisionError' in sys.stderr.getvalue()
-
-    def test_returning_value_from_delegated_throw(self):
-        """
-        Test returning value from delegated 'throw'
-        """
-        trace = []
-        class LunchError(Exception):
-            pass
-        d = dict(trace=trace, LunchError=LunchError)
-        exec('''if 1:
-        def g1():
-            try:
-                trace.append("Starting g1")
-                yield "g1 ham"
-                yield from g2()
-                yield "g1 eggs"
-            finally:
-                trace.append("Finishing g1")
-        def g2():
-            try:
-                trace.append("Starting g2")
-                yield "g2 spam"
-                yield "g2 more spam"


More information about the pypy-commit mailing list