summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYury Selivanov <yury@magic.io>2018-01-29 19:31:47 (GMT)
committerGitHub <noreply@github.com>2018-01-29 19:31:47 (GMT)
commit2a2270db9be9bdac5ffd2d50929bf905e7391a06 (patch)
tree4c45678f9e26ccc66843ed79a7467fabe03cf6b6
parentb647d7039d396b1da71ab33b101a78b53d4e6834 (diff)
downloadcpython-2a2270db9be9bdac5ffd2d50929bf905e7391a06.zip
cpython-2a2270db9be9bdac5ffd2d50929bf905e7391a06.tar.gz
cpython-2a2270db9be9bdac5ffd2d50929bf905e7391a06.tar.bz2
bpo-32703: Fix coroutine resource warning in case where there's an error (GH-5410)
The commit removes one unnecessary "if" clause in genobject.c. That "if" clause was masking un-awaited coroutines warnings just to make writing unittests more convenient.
-rw-r--r--Lib/test/test_coroutines.py96
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-01-29-01-15-17.bpo-32703.mwrF4-.rst2
-rw-r--r--Objects/genobject.c13
3 files changed, 72 insertions, 39 deletions
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 19a3444..f3d7719 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -520,34 +520,38 @@ class CoroutineTest(unittest.TestCase):
async def foo():
raise StopIteration
- with silence_coro_gc():
- self.assertRegex(repr(foo()), '^<coroutine object.* at 0x.*>$')
+ coro = foo()
+ self.assertRegex(repr(coro), '^<coroutine object.* at 0x.*>$')
+ coro.close()
def test_func_4(self):
async def foo():
raise StopIteration
+ coro = foo()
check = lambda: self.assertRaisesRegex(
TypeError, "'coroutine' object is not iterable")
with check():
- list(foo())
+ list(coro)
with check():
- tuple(foo())
+ tuple(coro)
with check():
- sum(foo())
+ sum(coro)
with check():
- iter(foo())
+ iter(coro)
- with silence_coro_gc(), check():
- for i in foo():
+ with check():
+ for i in coro:
pass
- with silence_coro_gc(), check():
- [i for i in foo()]
+ with check():
+ [i for i in coro]
+
+ coro.close()
def test_func_5(self):
@types.coroutine
@@ -560,8 +564,11 @@ class CoroutineTest(unittest.TestCase):
check = lambda: self.assertRaisesRegex(
TypeError, "'coroutine' object is not iterable")
+ coro = foo()
with check():
- for el in foo(): pass
+ for el in coro:
+ pass
+ coro.close()
# the following should pass without an error
for el in bar():
@@ -588,35 +595,53 @@ class CoroutineTest(unittest.TestCase):
def test_func_7(self):
async def bar():
return 10
+ coro = bar()
def foo():
- yield from bar()
-
- with silence_coro_gc(), self.assertRaisesRegex(
- TypeError,
- "cannot 'yield from' a coroutine object in a non-coroutine generator"):
+ yield from coro
+ with self.assertRaisesRegex(
+ TypeError,
+ "cannot 'yield from' a coroutine object in "
+ "a non-coroutine generator"):
list(foo())
+ coro.close()
+
def test_func_8(self):
@types.coroutine
def bar():
- return (yield from foo())
+ return (yield from coro)
async def foo():
return 'spam'
- self.assertEqual(run_async(bar()), ([], 'spam') )
+ coro = foo()
+ self.assertEqual(run_async(bar()), ([], 'spam'))
+ coro.close()
def test_func_9(self):
- async def foo(): pass
+ async def foo():
+ pass
with self.assertWarnsRegex(
- RuntimeWarning, "coroutine '.*test_func_9.*foo' was never awaited"):
+ RuntimeWarning,
+ r"coroutine '.*test_func_9.*foo' was never awaited"):
foo()
support.gc_collect()
+ with self.assertWarnsRegex(
+ RuntimeWarning,
+ r"coroutine '.*test_func_9.*foo' was never awaited"):
+
+ with self.assertRaises(TypeError):
+ # See bpo-32703.
+ for _ in foo():
+ pass
+
+ support.gc_collect()
+
def test_func_10(self):
N = 0
@@ -674,11 +699,14 @@ class CoroutineTest(unittest.TestCase):
def test_func_13(self):
async def g():
pass
+
+ coro = g()
with self.assertRaisesRegex(
- TypeError,
- "can't send non-None value to a just-started coroutine"):
+ TypeError,
+ "can't send non-None value to a just-started coroutine"):
+ coro.send('spam')
- g().send('spam')
+ coro.close()
def test_func_14(self):
@types.coroutine
@@ -977,8 +1005,6 @@ class CoroutineTest(unittest.TestCase):
return 42
async def foo():
- b = bar()
-
db = {'b': lambda: wrap}
class DB:
@@ -1023,19 +1049,21 @@ class CoroutineTest(unittest.TestCase):
def test_await_12(self):
async def coro():
return 'spam'
+ c = coro()
class Awaitable:
def __await__(self):
- return coro()
+ return c
async def foo():
return await Awaitable()
with self.assertRaisesRegex(
- TypeError, r"__await__\(\) returned a coroutine"):
-
+ TypeError, r"__await__\(\) returned a coroutine"):
run_async(foo())
+ c.close()
+
def test_await_13(self):
class Awaitable:
def __await__(self):
@@ -1991,14 +2019,15 @@ class SysSetCoroWrapperTest(unittest.TestCase):
finally:
with self.assertWarns(DeprecationWarning):
sys.set_coroutine_wrapper(None)
+ f.close()
with self.assertWarns(DeprecationWarning):
self.assertIsNone(sys.get_coroutine_wrapper())
wrapped = None
- with silence_coro_gc():
- foo()
+ coro = foo()
self.assertFalse(wrapped)
+ coro.close()
def test_set_wrapper_2(self):
with self.assertWarns(DeprecationWarning):
@@ -2022,11 +2051,12 @@ class SysSetCoroWrapperTest(unittest.TestCase):
sys.set_coroutine_wrapper(wrapper)
try:
with silence_coro_gc(), self.assertRaisesRegex(
- RuntimeError,
- r"coroutine wrapper.*\.wrapper at 0x.*attempted to "
- r"recursively wrap .* wrap .*"):
+ RuntimeError,
+ r"coroutine wrapper.*\.wrapper at 0x.*attempted to "
+ r"recursively wrap .* wrap .*"):
foo()
+
finally:
with self.assertWarns(DeprecationWarning):
sys.set_coroutine_wrapper(None)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-01-29-01-15-17.bpo-32703.mwrF4-.rst b/Misc/NEWS.d/next/Core and Builtins/2018-01-29-01-15-17.bpo-32703.mwrF4-.rst
new file mode 100644
index 0000000..cfd166b
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-01-29-01-15-17.bpo-32703.mwrF4-.rst
@@ -0,0 +1,2 @@
+Fix coroutine's ResourceWarning when there's an active error set when it's
+being finalized.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 1fdb57c..88b03c5 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -44,9 +44,10 @@ _PyGen_Finalize(PyObject *self)
PyObject *res = NULL;
PyObject *error_type, *error_value, *error_traceback;
- if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL)
+ if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL) {
/* Generator isn't paused, so no need to close */
return;
+ }
if (PyAsyncGen_CheckExact(self)) {
PyAsyncGenObject *agen = (PyAsyncGenObject*)self;
@@ -75,18 +76,18 @@ _PyGen_Finalize(PyObject *self)
issue a RuntimeWarning. */
if (gen->gi_code != NULL &&
((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
- gen->gi_frame->f_lasti == -1) {
- if (!error_value) {
- _PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
- }
+ gen->gi_frame->f_lasti == -1)
+ {
+ _PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
}
else {
res = gen_close(gen, NULL);
}
if (res == NULL) {
- if (PyErr_Occurred())
+ if (PyErr_Occurred()) {
PyErr_WriteUnraisable(self);
+ }
}
else {
Py_DECREF(res);