Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita7e2a10

Browse files
authored
gh-110771: Decompose run_forever() into parts (#110773)
Effectively introduce an unstable, private (really: protected) API for subclasses that want to override `run_forever()`.
1 parent0ed2329 commita7e2a10

File tree

4 files changed

+95
-31
lines changed

4 files changed

+95
-31
lines changed

‎Lib/asyncio/base_events.py‎

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,8 @@ def __init__(self):
400400
self._clock_resolution=time.get_clock_info('monotonic').resolution
401401
self._exception_handler=None
402402
self.set_debug(coroutines._is_debug_mode())
403+
# The preserved state of async generator hooks.
404+
self._old_agen_hooks=None
403405
# In debug mode, if the execution of a callback or a step of a task
404406
# exceed this duration in seconds, the slow callback/task is logged.
405407
self.slow_callback_duration=0.1
@@ -601,29 +603,52 @@ def _check_running(self):
601603
raiseRuntimeError(
602604
'Cannot run the event loop while another loop is running')
603605

604-
defrun_forever(self):
605-
"""Run until stop() is called."""
606+
def_run_forever_setup(self):
607+
"""Prepare the run loop to process events.
608+
609+
This method exists so that custom custom event loop subclasses (e.g., event loops
610+
that integrate a GUI event loop with Python's event loop) have access to all the
611+
loop setup logic.
612+
"""
606613
self._check_closed()
607614
self._check_running()
608615
self._set_coroutine_origin_tracking(self._debug)
609616

610-
old_agen_hooks=sys.get_asyncgen_hooks()
611-
try:
612-
self._thread_id=threading.get_ident()
613-
sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
614-
finalizer=self._asyncgen_finalizer_hook)
617+
self._old_agen_hooks=sys.get_asyncgen_hooks()
618+
self._thread_id=threading.get_ident()
619+
sys.set_asyncgen_hooks(
620+
firstiter=self._asyncgen_firstiter_hook,
621+
finalizer=self._asyncgen_finalizer_hook
622+
)
623+
624+
events._set_running_loop(self)
625+
626+
def_run_forever_cleanup(self):
627+
"""Clean up after an event loop finishes the looping over events.
615628
616-
events._set_running_loop(self)
629+
This method exists so that custom custom event loop subclasses (e.g., event loops
630+
that integrate a GUI event loop with Python's event loop) have access to all the
631+
loop cleanup logic.
632+
"""
633+
self._stopping=False
634+
self._thread_id=None
635+
events._set_running_loop(None)
636+
self._set_coroutine_origin_tracking(False)
637+
# Restore any pre-existing async generator hooks.
638+
ifself._old_agen_hooksisnotNone:
639+
sys.set_asyncgen_hooks(*self._old_agen_hooks)
640+
self._old_agen_hooks=None
641+
642+
defrun_forever(self):
643+
"""Run until stop() is called."""
644+
try:
645+
self._run_forever_setup()
617646
whileTrue:
618647
self._run_once()
619648
ifself._stopping:
620649
break
621650
finally:
622-
self._stopping=False
623-
self._thread_id=None
624-
events._set_running_loop(None)
625-
self._set_coroutine_origin_tracking(False)
626-
sys.set_asyncgen_hooks(*old_agen_hooks)
651+
self._run_forever_cleanup()
627652

628653
defrun_until_complete(self,future):
629654
"""Run until the Future is done.

‎Lib/asyncio/windows_events.py‎

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -314,24 +314,25 @@ def __init__(self, proactor=None):
314314
proactor=IocpProactor()
315315
super().__init__(proactor)
316316

317-
defrun_forever(self):
318-
try:
319-
assertself._self_reading_futureisNone
320-
self.call_soon(self._loop_self_reading)
321-
super().run_forever()
322-
finally:
323-
ifself._self_reading_futureisnotNone:
324-
ov=self._self_reading_future._ov
325-
self._self_reading_future.cancel()
326-
# self_reading_future was just cancelled so if it hasn't been
327-
# finished yet, it never will be (it's possible that it has
328-
# already finished and its callback is waiting in the queue,
329-
# where it could still happen if the event loop is restarted).
330-
# Unregister it otherwise IocpProactor.close will wait for it
331-
# forever
332-
ifovisnotNone:
333-
self._proactor._unregister(ov)
334-
self._self_reading_future=None
317+
def_run_forever_setup(self):
318+
assertself._self_reading_futureisNone
319+
self.call_soon(self._loop_self_reading)
320+
super()._run_forever_setup()
321+
322+
def_run_forever_cleanup(self):
323+
super()._run_forever_cleanup()
324+
ifself._self_reading_futureisnotNone:
325+
ov=self._self_reading_future._ov
326+
self._self_reading_future.cancel()
327+
# self_reading_future was just cancelled so if it hasn't been
328+
# finished yet, it never will be (it's possible that it has
329+
# already finished and its callback is waiting in the queue,
330+
# where it could still happen if the event loop is restarted).
331+
# Unregister it otherwise IocpProactor.close will wait for it
332+
# forever
333+
ifovisnotNone:
334+
self._proactor._unregister(ov)
335+
self._self_reading_future=None
335336

336337
asyncdefcreate_pipe_connection(self,protocol_factory,address):
337338
f=self._proactor.connect_pipe(address)

‎Lib/test/test_asyncio/test_base_events.py‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,43 @@ def test_run_forever_pre_stopped(self):
922922
self.loop.run_forever()
923923
self.loop._selector.select.assert_called_once_with(0)
924924

925+
deftest_custom_run_forever_integration(self):
926+
# Test that the run_forever_setup() and run_forever_cleanup() primitives
927+
# can be used to implement a custom run_forever loop.
928+
self.loop._process_events=mock.Mock()
929+
930+
count=0
931+
932+
defcallback():
933+
nonlocalcount
934+
count+=1
935+
936+
self.loop.call_soon(callback)
937+
938+
# Set up the custom event loop
939+
self.loop._run_forever_setup()
940+
941+
# Confirm the loop has been started
942+
self.assertEqual(asyncio.get_running_loop(),self.loop)
943+
self.assertTrue(self.loop.is_running())
944+
945+
# Our custom "event loop" just iterates 10 times before exiting.
946+
foriinrange(10):
947+
self.loop._run_once()
948+
949+
# Clean up the event loop
950+
self.loop._run_forever_cleanup()
951+
952+
# Confirm the loop has been cleaned up
953+
withself.assertRaises(RuntimeError):
954+
asyncio.get_running_loop()
955+
self.assertFalse(self.loop.is_running())
956+
957+
# Confirm the loop actually did run, processing events 10 times,
958+
# and invoking the callback once.
959+
self.assertEqual(self.loop._process_events.call_count,10)
960+
self.assertEqual(count,1)
961+
925962
asyncdefleave_unfinalized_asyncgen(self):
926963
# Create an async generator, iterate it partially, and leave it
927964
# to be garbage collected.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Expose the setup and cleanup portions of ``asyncio.run_forever()`` as the standalone methods ``asyncio.run_forever_setup()`` and ``asyncio.run_forever_cleanup()``. This allows for tighter integration with GUI event loops.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp