summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_coroutines.py
diff options
context:
space:
mode:
authorNathaniel J. Smith <njs@pobox.com>2018-01-21 14:44:07 (GMT)
committerYury Selivanov <yury@magic.io>2018-01-21 14:44:07 (GMT)
commitfc2f407829d9817ddacccae6944dd0879cfaca24 (patch)
tree1775a28a8181975363798f9b3e7cb2bb100e49a2 /Lib/test/test_coroutines.py
parent1211c9a9897a174b7261ca258cabf289815a40d8 (diff)
downloadcpython-fc2f407829d9817ddacccae6944dd0879cfaca24.zip
cpython-fc2f407829d9817ddacccae6944dd0879cfaca24.tar.gz
cpython-fc2f407829d9817ddacccae6944dd0879cfaca24.tar.bz2
bpo-32591: Add native coroutine origin tracking (#5250)
* Add coro.cr_origin and sys.set_coroutine_origin_tracking_depth * Use coroutine origin information in the unawaited coroutine warning * Stop using set_coroutine_wrapper in asyncio debug mode * In BaseEventLoop.set_debug, enable debugging in the correct thread
Diffstat (limited to 'Lib/test/test_coroutines.py')
-rw-r--r--Lib/test/test_coroutines.py131
1 files changed, 129 insertions, 2 deletions
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index d7d38a3..8a531b8 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -2,6 +2,7 @@ import contextlib
import copy
import inspect
import pickle
+import re
import sys
import types
import unittest
@@ -1974,9 +1975,11 @@ class SysSetCoroWrapperTest(unittest.TestCase):
wrapped = gen
return gen
- self.assertIsNone(sys.get_coroutine_wrapper())
+ with self.assertWarns(DeprecationWarning):
+ self.assertIsNone(sys.get_coroutine_wrapper())
- sys.set_coroutine_wrapper(wrap)
+ with self.assertWarns(DeprecationWarning):
+ sys.set_coroutine_wrapper(wrap)
self.assertIs(sys.get_coroutine_wrapper(), wrap)
try:
f = foo()
@@ -2041,6 +2044,130 @@ class SysSetCoroWrapperTest(unittest.TestCase):
sys.set_coroutine_wrapper(None)
+class OriginTrackingTest(unittest.TestCase):
+ def here(self):
+ info = inspect.getframeinfo(inspect.currentframe().f_back)
+ return (info.filename, info.lineno)
+
+ def test_origin_tracking(self):
+ orig_depth = sys.get_coroutine_origin_tracking_depth()
+ try:
+ async def corofn():
+ pass
+
+ sys.set_coroutine_origin_tracking_depth(0)
+ self.assertEqual(sys.get_coroutine_origin_tracking_depth(), 0)
+
+ with contextlib.closing(corofn()) as coro:
+ self.assertIsNone(coro.cr_origin)
+
+ sys.set_coroutine_origin_tracking_depth(1)
+ self.assertEqual(sys.get_coroutine_origin_tracking_depth(), 1)
+
+ fname, lineno = self.here()
+ with contextlib.closing(corofn()) as coro:
+ self.assertEqual(coro.cr_origin,
+ ((fname, lineno + 1, "test_origin_tracking"),))
+
+ sys.set_coroutine_origin_tracking_depth(2)
+ self.assertEqual(sys.get_coroutine_origin_tracking_depth(), 2)
+
+ def nested():
+ return (self.here(), corofn())
+ fname, lineno = self.here()
+ ((nested_fname, nested_lineno), coro) = nested()
+ with contextlib.closing(coro):
+ self.assertEqual(coro.cr_origin,
+ ((nested_fname, nested_lineno, "nested"),
+ (fname, lineno + 1, "test_origin_tracking")))
+
+ # Check we handle running out of frames correctly
+ sys.set_coroutine_origin_tracking_depth(1000)
+ with contextlib.closing(corofn()) as coro:
+ self.assertTrue(2 < len(coro.cr_origin) < 1000)
+
+ # We can't set depth negative
+ with self.assertRaises(ValueError):
+ sys.set_coroutine_origin_tracking_depth(-1)
+ # And trying leaves it unchanged
+ self.assertEqual(sys.get_coroutine_origin_tracking_depth(), 1000)
+
+ finally:
+ sys.set_coroutine_origin_tracking_depth(orig_depth)
+
+ def test_origin_tracking_warning(self):
+ async def corofn():
+ pass
+
+ a1_filename, a1_lineno = self.here()
+ def a1():
+ return corofn() # comment in a1
+ a1_lineno += 2
+
+ a2_filename, a2_lineno = self.here()
+ def a2():
+ return a1() # comment in a2
+ a2_lineno += 2
+
+ def check(depth, msg):
+ sys.set_coroutine_origin_tracking_depth(depth)
+ with warnings.catch_warnings(record=True) as wlist:
+ a2()
+ support.gc_collect()
+ # This might be fragile if other warnings somehow get triggered
+ # inside our 'with' block... let's worry about that if/when it
+ # happens.
+ self.assertTrue(len(wlist) == 1)
+ self.assertIs(wlist[0].category, RuntimeWarning)
+ self.assertEqual(msg, str(wlist[0].message))
+
+ orig_depth = sys.get_coroutine_origin_tracking_depth()
+ try:
+ msg = check(0, f"coroutine '{corofn.__qualname__}' was never awaited")
+ check(1, "".join([
+ f"coroutine '{corofn.__qualname__}' was never awaited\n",
+ "Coroutine created at (most recent call last)\n",
+ f' File "{a1_filename}", line {a1_lineno}, in a1\n',
+ f' return corofn() # comment in a1',
+ ]))
+ check(2, "".join([
+ f"coroutine '{corofn.__qualname__}' was never awaited\n",
+ "Coroutine created at (most recent call last)\n",
+ f' File "{a2_filename}", line {a2_lineno}, in a2\n',
+ f' return a1() # comment in a2\n',
+ f' File "{a1_filename}", line {a1_lineno}, in a1\n',
+ f' return corofn() # comment in a1',
+ ]))
+
+ finally:
+ sys.set_coroutine_origin_tracking_depth(orig_depth)
+
+ def test_unawaited_warning_when_module_broken(self):
+ # Make sure we don't blow up too bad if
+ # warnings._warn_unawaited_coroutine is broken somehow (e.g. because
+ # of shutdown problems)
+ async def corofn():
+ pass
+
+ orig_wuc = warnings._warn_unawaited_coroutine
+ try:
+ warnings._warn_unawaited_coroutine = lambda coro: 1/0
+ with support.captured_stderr() as stream:
+ corofn()
+ support.gc_collect()
+ self.assertIn("Exception ignored in", stream.getvalue())
+ self.assertIn("ZeroDivisionError", stream.getvalue())
+ self.assertIn("was never awaited", stream.getvalue())
+
+ del warnings._warn_unawaited_coroutine
+ with support.captured_stderr() as stream:
+ corofn()
+ support.gc_collect()
+ self.assertIn("was never awaited", stream.getvalue())
+
+ finally:
+ warnings._warn_unawaited_coroutine = orig_wuc
+
@support.cpython_only
class CAPITest(unittest.TestCase):