summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYury Selivanov <yury@magic.io>2018-01-26 20:24:24 (GMT)
committerGitHub <noreply@github.com>2018-01-26 20:24:24 (GMT)
commit43c47fe09640c579462978ec16f81295f5857cde (patch)
tree6a1524a805eb5d181acc04552bfb1a09ea736e39
parentdba976b8a28d6e5daa66ef31a6a7c694a9193f6a (diff)
downloadcpython-43c47fe09640c579462978ec16f81295f5857cde.zip
cpython-43c47fe09640c579462978ec16f81295f5857cde.tar.gz
cpython-43c47fe09640c579462978ec16f81295f5857cde.tar.bz2
bpo-32670: Enforce PEP 479. (#5327)
-rw-r--r--Doc/library/exceptions.rst14
-rw-r--r--Doc/whatsnew/3.7.rst6
-rw-r--r--Lib/test/test_generators.py22
-rw-r--r--Lib/test/test_with.py11
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-01-25-17-03-46.bpo-32670.YsqJUC.rst5
-rw-r--r--Objects/genobject.c58
-rw-r--r--Python/future.c2
7 files changed, 38 insertions, 80 deletions
diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst
index aa31410..42b99cc 100644
--- a/Doc/library/exceptions.rst
+++ b/Doc/library/exceptions.rst
@@ -367,17 +367,21 @@ The following exceptions are the exceptions that are usually raised.
raised, and the value returned by the function is used as the
:attr:`value` parameter to the constructor of the exception.
- If a generator function defined in the presence of a ``from __future__
- import generator_stop`` directive raises :exc:`StopIteration`, it will be
- converted into a :exc:`RuntimeError` (retaining the :exc:`StopIteration`
- as the new exception's cause).
+ If a generator code directly or indirectly raises :exc:`StopIteration`,
+ it is converted into a :exc:`RuntimeError` (retaining the
+ :exc:`StopIteration` as the new exception's cause).
.. versionchanged:: 3.3
Added ``value`` attribute and the ability for generator functions to
use it to return a value.
.. versionchanged:: 3.5
- Introduced the RuntimeError transformation.
+ Introduced the RuntimeError transformation via
+ ``from __future__ import generator_stop``, see :pep:`479`.
+
+ .. versionchanged:: 3.7
+ Enable :pep:`479` for all code by default: a :exc:`StopIteration`
+ error raised in a generator is transformed into a :exc:`RuntimeError`.
.. exception:: StopAsyncIteration
diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst
index a350919..43fbd01 100644
--- a/Doc/whatsnew/3.7.rst
+++ b/Doc/whatsnew/3.7.rst
@@ -935,6 +935,12 @@ that may require changes to your code.
Changes in Python behavior
--------------------------
+* :pep:`479` is enabled for all code in Python 3.7, meaning that
+ :exc:`StopIteration` exceptions raised directly or indirectly in
+ coroutines and generators are transformed into :exc:`RuntimeError`
+ exceptions.
+ (Contributed by Yury Selivanov in :issue:`32670`.)
+
* Due to an oversight, earlier Python versions erroneously accepted the
following syntax::
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index bd17ad4..3461f20 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -265,26 +265,16 @@ class ExceptionTest(unittest.TestCase):
self.assertEqual(next(g), "done")
self.assertEqual(sys.exc_info(), (None, None, None))
- def test_stopiteration_warning(self):
+ def test_stopiteration_error(self):
# See also PEP 479.
def gen():
raise StopIteration
yield
- with self.assertRaises(StopIteration), \
- self.assertWarnsRegex(DeprecationWarning, "StopIteration"):
-
- next(gen())
-
- with self.assertRaisesRegex(DeprecationWarning,
- "generator .* raised StopIteration"), \
- warnings.catch_warnings():
-
- warnings.simplefilter('error')
+ with self.assertRaisesRegex(RuntimeError, 'raised StopIteration'):
next(gen())
-
def test_tutorial_stopiteration(self):
# Raise StopIteration" stops the generator too:
@@ -296,13 +286,7 @@ class ExceptionTest(unittest.TestCase):
g = f()
self.assertEqual(next(g), 1)
- with self.assertWarnsRegex(DeprecationWarning, "StopIteration"):
- with self.assertRaises(StopIteration):
- next(g)
-
- with self.assertRaises(StopIteration):
- # This time StopIteration isn't raised from the generator's body,
- # hence no warning.
+ with self.assertRaisesRegex(RuntimeError, 'raised StopIteration'):
next(g)
def test_return_tuple(self):
diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py
index c70f685..b1d7a15 100644
--- a/Lib/test/test_with.py
+++ b/Lib/test/test_with.py
@@ -458,8 +458,8 @@ class ExceptionalTestCase(ContextmanagerAssertionMixin, unittest.TestCase):
with cm():
raise StopIteration("from with")
- with self.assertWarnsRegex(DeprecationWarning, "StopIteration"):
- self.assertRaises(StopIteration, shouldThrow)
+ with self.assertRaisesRegex(StopIteration, 'from with'):
+ shouldThrow()
def testRaisedStopIteration2(self):
# From bug 1462485
@@ -473,7 +473,8 @@ class ExceptionalTestCase(ContextmanagerAssertionMixin, unittest.TestCase):
with cm():
raise StopIteration("from with")
- self.assertRaises(StopIteration, shouldThrow)
+ with self.assertRaisesRegex(StopIteration, 'from with'):
+ shouldThrow()
def testRaisedStopIteration3(self):
# Another variant where the exception hasn't been instantiated
@@ -486,8 +487,8 @@ class ExceptionalTestCase(ContextmanagerAssertionMixin, unittest.TestCase):
with cm():
raise next(iter([]))
- with self.assertWarnsRegex(DeprecationWarning, "StopIteration"):
- self.assertRaises(StopIteration, shouldThrow)
+ with self.assertRaises(StopIteration):
+ shouldThrow()
def testRaisedGeneratorExit1(self):
# From bug 1462485
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-01-25-17-03-46.bpo-32670.YsqJUC.rst b/Misc/NEWS.d/next/Core and Builtins/2018-01-25-17-03-46.bpo-32670.YsqJUC.rst
new file mode 100644
index 0000000..b22249b
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-01-25-17-03-46.bpo-32670.YsqJUC.rst
@@ -0,0 +1,5 @@
+Enforce PEP 479 for all code.
+
+This means that manually raising a StopIteration exception from a generator
+is prohibited for all code, regardless of whether 'from __future__ import
+generator_stop' was used or not.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 7baffa7..1fdb57c 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -248,59 +248,17 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
Py_CLEAR(result);
}
else if (!result && PyErr_ExceptionMatches(PyExc_StopIteration)) {
- /* Check for __future__ generator_stop and conditionally turn
- * a leaking StopIteration into RuntimeError (with its cause
- * set appropriately). */
-
- const int check_stop_iter_error_flags = CO_FUTURE_GENERATOR_STOP |
- CO_COROUTINE |
- CO_ITERABLE_COROUTINE |
- CO_ASYNC_GENERATOR;
-
- if (gen->gi_code != NULL &&
- ((PyCodeObject *)gen->gi_code)->co_flags &
- check_stop_iter_error_flags)
- {
- /* `gen` is either:
- * a generator with CO_FUTURE_GENERATOR_STOP flag;
- * a coroutine;
- * a generator with CO_ITERABLE_COROUTINE flag
- (decorated with types.coroutine decorator);
- * an async generator.
- */
- const char *msg = "generator raised StopIteration";
- if (PyCoro_CheckExact(gen)) {
- msg = "coroutine raised StopIteration";
- }
- else if PyAsyncGen_CheckExact(gen) {
- msg = "async generator raised StopIteration";
- }
- _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
+ const char *msg = "generator raised StopIteration";
+ if (PyCoro_CheckExact(gen)) {
+ msg = "coroutine raised StopIteration";
}
- else {
- /* `gen` is an ordinary generator without
- CO_FUTURE_GENERATOR_STOP flag.
- */
-
- PyObject *exc, *val, *tb;
-
- /* Pop the exception before issuing a warning. */
- PyErr_Fetch(&exc, &val, &tb);
-
- if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
- "generator '%.50S' raised StopIteration",
- gen->gi_qualname)) {
- /* Warning was converted to an error. */
- Py_XDECREF(exc);
- Py_XDECREF(val);
- Py_XDECREF(tb);
- }
- else {
- PyErr_Restore(exc, val, tb);
- }
+ else if PyAsyncGen_CheckExact(gen) {
+ msg = "async generator raised StopIteration";
}
+ _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg);
+
}
- else if (PyAsyncGen_CheckExact(gen) && !result &&
+ else if (!result && PyAsyncGen_CheckExact(gen) &&
PyErr_ExceptionMatches(PyExc_StopAsyncIteration))
{
/* code in `gen` raised a StopAsyncIteration error:
diff --git a/Python/future.c b/Python/future.c
index 53faa6b..ade647f 100644
--- a/Python/future.c
+++ b/Python/future.c
@@ -41,7 +41,7 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, PyObject *filename)
} else if (strcmp(feature, FUTURE_BARRY_AS_BDFL) == 0) {
ff->ff_features |= CO_FUTURE_BARRY_AS_BDFL;
} else if (strcmp(feature, FUTURE_GENERATOR_STOP) == 0) {
- ff->ff_features |= CO_FUTURE_GENERATOR_STOP;
+ continue;
} else if (strcmp(feature, FUTURE_ANNOTATIONS) == 0) {
ff->ff_features |= CO_FUTURE_ANNOTATIONS;
} else if (strcmp(feature, "braces") == 0) {