gh-110771: Decompose run_forever() into parts (#110773)

Effectively introduce an unstable, private (really: protected) API for subclasses that want to override `run_forever()`.
This commit is contained in:
Russell Keith-Magee 2023-10-13 16:12:32 +02:00 committed by GitHub
parent 0ed2329a16
commit a7e2a10a85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 31 deletions

View file

@ -400,6 +400,8 @@ class BaseEventLoop(events.AbstractEventLoop):
self._clock_resolution = time.get_clock_info('monotonic').resolution
self._exception_handler = None
self.set_debug(coroutines._is_debug_mode())
# The preserved state of async generator hooks.
self._old_agen_hooks = None
# In debug mode, if the execution of a callback or a step of a task
# exceed this duration in seconds, the slow callback/task is logged.
self.slow_callback_duration = 0.1
@ -601,29 +603,52 @@ class BaseEventLoop(events.AbstractEventLoop):
raise RuntimeError(
'Cannot run the event loop while another loop is running')
def run_forever(self):
"""Run until stop() is called."""
def _run_forever_setup(self):
"""Prepare the run loop to process events.
This method exists so that custom custom event loop subclasses (e.g., event loops
that integrate a GUI event loop with Python's event loop) have access to all the
loop setup logic.
"""
self._check_closed()
self._check_running()
self._set_coroutine_origin_tracking(self._debug)
old_agen_hooks = sys.get_asyncgen_hooks()
try:
self._thread_id = threading.get_ident()
sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
finalizer=self._asyncgen_finalizer_hook)
self._old_agen_hooks = sys.get_asyncgen_hooks()
self._thread_id = threading.get_ident()
sys.set_asyncgen_hooks(
firstiter=self._asyncgen_firstiter_hook,
finalizer=self._asyncgen_finalizer_hook
)
events._set_running_loop(self)
events._set_running_loop(self)
def _run_forever_cleanup(self):
"""Clean up after an event loop finishes the looping over events.
This method exists so that custom custom event loop subclasses (e.g., event loops
that integrate a GUI event loop with Python's event loop) have access to all the
loop cleanup logic.
"""
self._stopping = False
self._thread_id = None
events._set_running_loop(None)
self._set_coroutine_origin_tracking(False)
# Restore any pre-existing async generator hooks.
if self._old_agen_hooks is not None:
sys.set_asyncgen_hooks(*self._old_agen_hooks)
self._old_agen_hooks = None
def run_forever(self):
"""Run until stop() is called."""
try:
self._run_forever_setup()
while True:
self._run_once()
if self._stopping:
break
finally:
self._stopping = False
self._thread_id = None
events._set_running_loop(None)
self._set_coroutine_origin_tracking(False)
sys.set_asyncgen_hooks(*old_agen_hooks)
self._run_forever_cleanup()
def run_until_complete(self, future):
"""Run until the Future is done.