-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
bpo-34037: Fix test_asyncio failure and add loop.shutdown_default_executor() #15735
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -0,0 +1 @@ | |||
For :mod:`asyncio`, add attribute ``asyncio.BaseEventLoop.wait_executor_on_close`` to provide an option for waiting for the executor before closing the event loop. Also adds methods ``loop.get_wait_executor_on_close`` and ``loop.set_wait_executor_on_close``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll fix the number of characters per line and add the appropriate Sphinx roles after I finish adding the documentation changes.
Adding a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have to update the documentation:
- Document the 2 new event loop methods, around https://docs.python.org/dev/library/asyncio-eventloop.html
- Document the behavior change in What's New in Python 3.9, around: https://docs.python.org/dev/whatsnew/3.9.html#changes-in-the-python-api
Misc/NEWS.d/next/Library/2019-09-09-04-43-26.bpo-34037.kX3ZqZ.rst
Outdated
Show resolved
Hide resolved
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
Thanks for the feedback @vstinner.
Yep, I was definitely planning on doing so within this PR. I just wanted to make sure the implementation details were satisfactory first, so that I wouldn't have to add the documentation changes and then completely rewrite them from different specifications. I'll start working on that next. I appreciate that you specified the locations. I figured the documentation for the methods would be on asyncio event loop page, but I wasn't certain where to place the changes in What's New (or whether that should be done within this PR). This will be my first time adding an entry in there, as the majority of my contributions thus far have been PR reviews or documentation changes. I have to admit, adding my own What's New entry for 3.9 is both a little intimidating and exciting. (: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't like this API and I'd suggest to implement it the other way around. We have a very similar problem with asynchronous generators -- closing them isn't an easy process and can be a lengthy blocking operation. The way we do that is via the loop.shutdown_asyncgens()
method, that users are supposed to call manually before loop.close()
. This is a low-level API, and the expectation is that asyncio.run()
function would do that for you.
Therefore I propose the following:
-
Add a new
loop.shutdown_threadpool()
method. Just like withshutdown_asyncgens()
-- it would be invalid to callloop.run_in_executer()
afterloop.shutdown_threadpool()
is called. -
Make
asyncio.run()
to call the newloop.shutdown_threadpool()
.
@aeros167 Kyle, do you have time to work on this issue this week? |
Yeah, I definitely should have time to work on it. Would you recommend making the changes directly to this PR or opening a separate one? I think your proposal would be far more robust and provide a significantly better user experience. But this change might be a decent fallback, mainly for the purpose of fixing the CI tests in case there are unforeseen complications with properly implementing the proposed I would be glad to help with working on implementing it though. I've been interested in becoming more involved with improving the CI tests and asyncio development in general, and I think this process will provide a great introduction to both. Thanks for the in-depth feedback. |
I support @1st1 proposal: |
Well, it's a bit too late to add new APIs in the 3.8 branch, no matter if they are public or private. I suggest to go ahead with the new idea so that we can fix the CI for the master branch. FWIW @vstinner suggested the |
My current plan:
def shutdown_default_executor(self, wait=True):
# Used by loop.run_in_executer() to check if the the executor has been terminated
self._executor_shutdown_called = True
executor = self._default_executor
# Nothing to shut down if there is no executor
if executor is not None:
self._default_executor = None
executor.shutdown(wait) # alternatively, wait=wait, but that seems redundant
def _check_executor_shutdown(self):
if self._executor_shutdown_called:
raise RuntimeError("Executor has been shut down")
def close(self):
...
self._closed = True
self._ready.clear()
self._scheduled.clear()
# executor = self._default_executor
# if executor is not None:
# self._default_executor = None
# executor.shutdown(wait=self._wait_executor_on_close)
self.shutdown_default_executor() # default is True In the above implementation, users could directly utilize If further customization is required (or there's issues with the tests timing out in the future), we could add an optional argument thread_timeout to
If the above is satisfactory, +1 this comment, otherwise let me know if anything is incorrect. Note: Several of the comments in the examples above are for the purpose of explaining my thought process, and would not be included in the PR. Particularly the inline comments. |
I'll change the PR title as well after I commit the changes to the PR branch. I've got some college work to do for now, but I should be able to work on that later tonight or tomorrow. |
|
That would make more sense, especially since this is done with
Hmm, okay. It just seemed odd to me to leave in the part in the last 4 lines of I'm not certain where the best place to call ...
loop = events.new_event_loop()
try:
events.set_event_loop(loop)
loop.set_debug(debug)
return loop.run_until_complete(main)
finally:
try:
_cancel_all_tasks(loop)
loop.run_until_complete(loop.shutdown_asyncgens())
loop.shutdown_default_executor()
finally:
events.set_event_loop(None)
loop.close() Thanks for the feedback, and I greatly appreciate that you guys have allowed me to help with working on this issue. I've been learning a lot about the internals of asyncio during this process. (: |
I think your last comment is correct. |
Updating the title to better reflect the changes and purpose of the PR. I'll update the PR itself after receiving approval from Yury on the implementation details. |
I just realized that I forgot to set |
They should remain as-is. The reasoning:
The Also, I'd make |
@1st1 I understand the motivation but curious how it can be done in practice. Waiting is a synchronous call. |
I'd just keep it simple and let the coroutine block. We can implement the "wait in a thread" solution later if we need to. Do you think this would be a bad idea? |
Oh crap, I just messed that up. I was attempting to update only the master branch in my fork, I did not mean to affect this PR branch. I'll try to revert this branch back to dbc08ae. Apologies for the spam messages, I'm still fairly new to using Git. I must have accidentally left it in the bpo-34037 branch when I made the changes instead of switching to master... |
Fixed, I successfully reverted the PR branch to dbc08ae. I would appreciate it if someone could remove the additional reviewers added to the PR, I don't have permission to do that. Sorry about the mess. |
done |
Normally, I haven't left the "Patch by " bit in the news entries for my own PRs, but since this one involved a significant amount of effort on my part, I'd like to do so. I'll update the news entry accordingly. |
Let's merge it, there is no reason for waiting |
The only reason to wait was getting feedback from @1st1 and @vstinner on whether or not the "What's New" entry should be added, but that can be done at a later point if it's useful/necessary. All of the actual changes were finalized. Thanks for merging it! |
New features deserve an entry in the What's New (In Python 3.8) doc, yes. You can open a new PR for that. |
Based on a comment from @asvetlov #15735 (comment), this removes the provisional note for ``asyncio.run()`` in the documentation. Automerge-Triggered-By: @1st1
This PR was based heavily upon @vstinner's PR and the suggestions from @asvetlov to improve upon it:
@vstinner's PR was reverted due to it making too drastic of an API change for 3.8 and the concerns from @asvetlov about adding the property without providing public getter and setter methods.
However, the changes proposed still had significant support, and most importantly, it fixes a bug which has been causing intermittent failures within the CI tests for
test_asyncio
due to dangling threads:Since this is involving changes to the public API, I fully plan on adding the appropriate changes to the documentation. But, I would prefer to wait until I receive feedback from @vstinner and @asvetlov about the implementation itself before doing so since they were both significantly involved in @vstinner's previous PR.
I'll cc the asyncio team as well in case anyone else has feedback to provide.
/cc @python/asyncio
https://bugs.python.org/issue34037