-
Notifications
You must be signed in to change notification settings - Fork 95
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
Workaround for asyncio.get_event_loop() deprecation #355
Workaround for asyncio.get_event_loop() deprecation #355
Conversation
Codecov Report
@@ Coverage Diff @@
## master #355 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 7 7
Lines 1696 1706 +10
Branches 310 310
=========================================
+ Hits 1696 1706 +10
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
8f1fd49
to
718c45a
Compare
aiosmtpd/__init__.py
Outdated
warnings.simplefilter("ignore") | ||
try: | ||
loop = asyncio.get_event_loop() | ||
except (DeprecationWarning, RuntimeError): # pragma: no cover |
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.
When 3.12 comes out we might have to revisit this and change the caught exception in accordance to the actual exception that will be raised.
Is there any sensible write-up as per why (and perhaps when) this policy stuff was introduced with asyncio? |
The deprecation notice first appeared with Python 3.10: https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop There's a discussion about this here: python/cpython#83710 What's being discussed is over my head atm, though. So I'm just taking the 'easy way out' trying to ensure compat by implementing a workaround. And one ugly warning suppression. Edit: More dicussion here: python/cpython#93453 |
I hope @asvetlov can chime in here. I'm not sure where things are going, and I'd like this PR to be both backward- and forward-compatible with Python >= 3.7 The current changes indeed pass green when fed through the GHCI testing fleet, but it's still making me uneasy. |
I've taken a quick glance at this and based on the discussion I'm pretty sure that we need to make a decision within aiosmtpd: Do we want to take responsibility for the created event loop? It's looking like historically that's caused problems for folks generally. I think in our case we have two different scenarios:
What that looks like, I think, is that we should use Comments, questions, rebuttals? |
I'm all for treating However that will introduce some possibly-breaking changes, especially for users that currently uses So if we want to to go "pure library" route -- which for the record I'm all for it -- that change will need to wait until at the very least 1.5.0 ... or even 1.6.0 or 2.0.0 This PR is just kind of a band-aid over the underlying problem. At the very least it's meant to provide compat for 3.7 <= Python <= 3.12 |
It looks like get_running_loop exists in 3.7 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_running_loop We should be able to use that - and if we want to retain compatibility just catch RuntimeError and start our own event loop. For the record, I'm entirely OK with having entrypoints (e.g. http.server) that will actually spin up loops, we just need to do that in a responsible way. |
There's a catch though:
Some calls to |
That import was used only to get the current event loop. Now we handle that in __init__.py
Also simplify the line because we have a helper func now
Because at the point the expected behavior is not yet happening.
718c45a
to
502852a
Compare
Because apparently in Python<3.12, even though get_event_loop() raises a warning, it still returns existing event loop. The behavior is expected to change on Python>=3.12, so let's cut off at that point.
try: | ||
loop = asyncio.get_event_loop() | ||
except (DeprecationWarning, RuntimeError): # pragma: py-lt-310 | ||
if loop is None: # pragma: py-lt-312 |
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.
On Python 3.11 (and I believe also 3.10), though get_event_loop()
raises a warning when there's no existing event loop, it does indeed return a newly-created event loop. This defensive check prevents (re-)creation of a new event loop in that situation.
On Python 3.12 the behavior likely changes, i.e., no implicit creation of event loop inside get_event_loop()
. In that situation, loop
will remain None
and only in that case will we create a new event loop.
That is why this particular check has a # pragma: py-lt-312
mark there, because the branch will never be taken for Python<3.12
Aaaaand it seems that the new Warning behavior is getting reverted? Nevertheless I still want to merge this changes to Cover-Our-Posteriors, so to speak. Edit: If indeed removal gets rescheduled as Guido informed here we will need to revisit the |
Well, they're just deferred, and will eventually fail. I only glanced at this, if you'd like me to do a more thorough review I can, otherwise I'm OK with this approach for now |
Yeah, this PR basically just put a wrapper around asyncio.get_event_loop() to maintain the behavior of the function up to Python 3.9. So just a band-aid to the deeper problems. I'd say for long term, just like you mentioned, we need to take a step back and implement asyncio more properly. And in the process probably we can also handle Issue #334 by using the 'more modern' way of using asyncio. |
This PR is not really 'urgent', though I've heard some complaints about the DeprecationWarning. I'll let this simmer a bit for several more days just to ensure that I haven't overlooked something... |
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 didn't dig to check for places this should have been used or anything, but generally I do like this, and don't see any problems.
if loop is None: # pragma: py-lt-312 | ||
loop = asyncio.new_event_loop() | ||
asyncio.set_event_loop(loop) | ||
assert isinstance(loop, asyncio.AbstractEventLoop) |
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.
comment FWIW this is probably less useful, since assertions can be disabled. And everything will go sideways shortly after this function gets called anyway 😂
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.
It's mostly for type checking, not expected to do anything useful 😂
What do these changes do?
Implement workaround due to the deprecation in behavior of asyncio.get_event_loop() (which will outright result in error with Python 3.12)
This change of behavior first appeared in Python 3.10: https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop
And there has been some discussion on this, among others:
Are there changes in behavior for the user?
Should be minimal.
get_event_loop_policy()
was in effect at time of instantiation.uvloop
)Related issue number
Closes #353
Checklist
py311-{nocov,cov}