Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
gh-110771: Decompose run_forever() into parts#110773
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from10 commits
a0c1fde67bfc8f3dcf97b35271dc6672f6854349c3e6226a523852d0e7f892ef94494431431261b3e3d8File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -210,6 +210,56 @@ Running and stopping the loop | ||
| .. versionchanged:: 3.12 | ||
| Added the *timeout* parameter. | ||
| .. method:: loop.run_forever_setup() | ||
| Set up an event loop so that it is ready to start actively looping and | ||
| processing events. | ||
| .. note:: | ||
| End users should not use this method directly. This method is only needed | ||
| if you are writing your own ``EventLoop`` subclass, with a customized | ||
| event processing loop. For example, if you are integrating Python's | ||
| asyncio event loop with a GUI library's event loop, you may need to write | ||
| a customized :meth:`loop.run_forever` implementation that accommodates | ||
| both CPython's event loop and the GUI library's event loop. You can use | ||
| this method to ensure that Python's event loop is correctly configured and | ||
| ready to start processing events. | ||
| The specific details of a customized ``EventLoop`` subclass will depend | ||
| on the GUI library you are integrating with. However, the broad structure | ||
| of a custom ``EventLoop`` would look something like:: | ||
| class CustomGUIEventLoop(EventLoop): | ||
| def run_forever(self): | ||
| try: | ||
| self.run_forever_setup() | ||
| gui_library.setup() | ||
| while True: | ||
| self._run_once() | ||
| gui_library.process_events() | ||
| if self._stopping: | ||
| ||
| break | ||
| finally: | ||
| self.run_forever_cleanup() | ||
| gui_library.cleanup() | ||
| ||
| .. versionadded:: 3.13 | ||
| .. method:: loop.run_forever_cleanup() | ||
| Perform any cleanup necessary at the conclusion of event processing to ensure | ||
| that the event loop has been fully shut down. | ||
| ||
| .. note:: | ||
| End users should not use this method directly. This method is only needed | ||
| if you are writing your own ``EventLoop`` subclass, with a customized | ||
| inner event processing loop. See :meth:`loop.run_forever_setup()` for | ||
| details on why and how to use this method. | ||
| .. versionadded:: 3.13 | ||
| Scheduling callbacks | ||
| ^^^^^^^^^^^^^^^^^^^^ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -400,6 +400,8 @@ def __init__(self): | ||
| 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 @@ def _check_running(self): | ||
| raise RuntimeError( | ||
| 'Cannot run the event loop while another loop is running') | ||
| def run_forever_setup(self): | ||
| """Prepare the run loop to process events. | ||
| This method should be used as part of the ``run_forever()`` | ||
| implementation in a custom event loop subclass (e.g., integrating a GUI | ||
| event loop with Python's event loop). | ||
| """ | ||
| self._check_closed() | ||
| self._check_running() | ||
| self._set_coroutine_origin_tracking(self._debug) | ||
| 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) | ||
| def run_forever_cleanup(self): | ||
| """Clean up after an event loop finishes the looping over events. | ||
| This method should be used as part of the ``run_forever()`` | ||
| implementation in a custom event loop subclass (e.g., integrating a GUI | ||
| event loop with Python's event loop). | ||
| """ | ||
| 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.""" | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Maybe clarify in this docstring that this is what calls setup/cleanup? | ||
| try: | ||
| self.run_forever_setup() | ||
| while True: | ||
| self._run_once() | ||
| if self._stopping: | ||
| break | ||
| finally: | ||
| self.run_forever_cleanup() | ||
| def run_until_complete(self, future): | ||
| """Run until the Future is done. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -314,24 +314,25 @@ def __init__(self, proactor=None): | ||
| proactor = IocpProactor() | ||
| super().__init__(proactor) | ||
| def run_forever_setup(self): | ||
| assert self._self_reading_future is None | ||
| self.call_soon(self._loop_self_reading) | ||
| super().run_forever_setup() | ||
gvanrossum marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| def run_forever_cleanup(self): | ||
| super().run_forever_cleanup() | ||
| if self._self_reading_future is not None: | ||
| ov = self._self_reading_future._ov | ||
| self._self_reading_future.cancel() | ||
| # self_reading_future was just cancelled so if it hasn't been | ||
| # finished yet, it never will be (it's possible that it has | ||
| # already finished and its callback is waiting in the queue, | ||
| # where it could still happen if the event loop is restarted). | ||
| # Unregister it otherwise IocpProactor.close will wait for it | ||
| # forever | ||
| if ov is not None: | ||
| self._proactor._unregister(ov) | ||
| self._self_reading_future = None | ||
| async def create_pipe_connection(self, protocol_factory, address): | ||
| f = self._proactor.connect_pipe(address) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +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. |