[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