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
This commit is contained in:
Nathaniel J. Smith 2018-01-21 06:44:07 -08:00 committed by Yury Selivanov
parent 1211c9a989
commit fc2f407829
20 changed files with 485 additions and 100 deletions

View file

@ -34,6 +34,7 @@ try:
except ImportError: # pragma: no cover
ssl = None
from . import constants
from . import coroutines
from . import events
from . import futures
@ -224,7 +225,8 @@ class BaseEventLoop(events.AbstractEventLoop):
self.slow_callback_duration = 0.1
self._current_handle = None
self._task_factory = None
self._coroutine_wrapper_set = False
self._coroutine_origin_tracking_enabled = False
self._coroutine_origin_tracking_saved_depth = None
if hasattr(sys, 'get_asyncgen_hooks'):
# Python >= 3.6
@ -382,7 +384,7 @@ class BaseEventLoop(events.AbstractEventLoop):
if events._get_running_loop() is not None:
raise RuntimeError(
'Cannot run the event loop while another loop is running')
self._set_coroutine_wrapper(self._debug)
self._set_coroutine_origin_tracking(self._debug)
self._thread_id = threading.get_ident()
if self._asyncgens is not None:
old_agen_hooks = sys.get_asyncgen_hooks()
@ -398,7 +400,7 @@ class BaseEventLoop(events.AbstractEventLoop):
self._stopping = False
self._thread_id = None
events._set_running_loop(None)
self._set_coroutine_wrapper(False)
self._set_coroutine_origin_tracking(False)
if self._asyncgens is not None:
sys.set_asyncgen_hooks(*old_agen_hooks)
@ -1531,39 +1533,20 @@ class BaseEventLoop(events.AbstractEventLoop):
handle._run()
handle = None # Needed to break cycles when an exception occurs.
def _set_coroutine_wrapper(self, enabled):
try:
set_wrapper = sys.set_coroutine_wrapper
get_wrapper = sys.get_coroutine_wrapper
except AttributeError:
def _set_coroutine_origin_tracking(self, enabled):
if bool(enabled) == bool(self._coroutine_origin_tracking_enabled):
return
enabled = bool(enabled)
if self._coroutine_wrapper_set == enabled:
return
wrapper = coroutines.debug_wrapper
current_wrapper = get_wrapper()
if enabled:
if current_wrapper not in (None, wrapper):
warnings.warn(
f"loop.set_debug(True): cannot set debug coroutine "
f"wrapper; another wrapper is already set "
f"{current_wrapper!r}",
RuntimeWarning)
else:
set_wrapper(wrapper)
self._coroutine_wrapper_set = True
self._coroutine_origin_tracking_saved_depth = (
sys.get_coroutine_origin_tracking_depth())
sys.set_coroutine_origin_tracking_depth(
constants.DEBUG_STACK_DEPTH)
else:
if current_wrapper not in (None, wrapper):
warnings.warn(
f"loop.set_debug(False): cannot unset debug coroutine "
f"wrapper; another wrapper was set {current_wrapper!r}",
RuntimeWarning)
else:
set_wrapper(None)
self._coroutine_wrapper_set = False
sys.set_coroutine_origin_tracking_depth(
self._coroutine_origin_tracking_saved_depth)
self._coroutine_origin_tracking_enabled = enabled
def get_debug(self):
return self._debug
@ -1572,4 +1555,4 @@ class BaseEventLoop(events.AbstractEventLoop):
self._debug = enabled
if self.is_running():
self._set_coroutine_wrapper(enabled)
self.call_soon_threadsafe(self._set_coroutine_origin_tracking, enabled)