Skip to content

Add a compatibility layer for the asyncio changes in Python 3.14.0a4.#558

Merged
freakboy3742 merged 3 commits intomainfrom
py314-compat
Jan 29, 2025
Merged

Add a compatibility layer for the asyncio changes in Python 3.14.0a4.#558
freakboy3742 merged 3 commits intomainfrom
py314-compat

Conversation

@freakboy3742
Copy link
Member

@freakboy3742 freakboy3742 commented Jan 27, 2025

Python 3.14.0a4 deprecated the use of custom EventLoopPolicy instances, in favor of instantiating custom event loops directly. This PR adapts to that change by:

  1. Aliasing the imports of the EventLoopPolicy base classes to their new "internal" names. This solves the immediate testing issue that was causing failures in CI.
  2. Adapts to a name change in iscoroutinefunction that raises now warnings in Python 3.14.
  3. Adding RubiconEventLoop() as a public API. On Python 3.14+, this is the CFEventLoop class; on earlier versions, it is a wrapper method that creates and installs an event loop policy, then creates a new event loop.

This approach should be backwards compatible. Any code using the old policy-based approach will continue to work, but will raise a DeprecationWarning advising users to switch to the RubiconEventLoop() approach. Code using the RubiconEventLoop() approach will work on all supported versions of Python, and won't raise a warning (even though technically it is using the deprecated EventLoopPolicy class.

Fixes #557.

PR Checklist:

  • All new features have been tested
  • All new features have been documented
  • I have read the CONTRIBUTING.md file
  • I will abide by the code of conduct

)
policy = EventLoopPolicy()
set_event_loop_policy(policy)
return new_event_loop()
Copy link
Member

@samschott samschott Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect rubicon-objc to be typically used in cases where it's ok to set the event loop policy globally. However, it might be a bit less invasive and more compatible with the Python 3.14 approach if we return the event loop instance without any side effects. For example:

policy = EventLoopPolicy()
return policy.get_event_loop()

WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... get_event_loop() has been deprecated as well (and has been for a couple of releases).

The Python PR that triggered this change documents the "suggested" API usage as:

loop = MyCustomEventLoop()
loop.run_until_complete(task)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... python/cpython#83710 (and has been for a couple of releases).

How about the following then?

policy = EventLoopPolicy()
return policy.new_event_loop()

This might be stupid question, but why special case Python < 3.14 at all? Could we use the "suggested" API usage and instantiate the CFEventLoop directly on older Python versions as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the following then?

policy = EventLoopPolicy()
return policy.new_event_loop()

That's functionally identical to asyncio.new_event_loop() - but I guess it doesn't hurt to be explicit about which policy is creating the loop and avoiding a function call.

This might be stupid question, but why special case Python < 3.14 at all? Could we use the "suggested" API usage and instantiate the CFEventLoop directly on older Python versions as well?

In < 3.14, there are still some functions on the policy - child watchers, mechanisms to get the default loop, and so on. For maximum compatibility, I figured it was best to preserve the old API until it is fully deprecated.

from asyncio import (
_AbstractEventLoopPolicy as AbstractEventLoopPolicy,
_DefaultEventLoopPolicy as DefaultEventLoopPolicy,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume those imports will stop working with Python 3.16. Is there anything we can do to prepare for this now in addition to the deprecation warning below?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure - since we know the deprecation will be finalised with 3.16, we can make these imports and the actual EventLoopPolicy definition conditional on version_info < (3, 16). That way, when, in 6 years time, we run the pyupgrade with the 3.16 minimum version, all the dead code will be automatically removed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! No strong opinions here, I'll leave it to you whether you think thats worth doing :)

@freakboy3742 freakboy3742 merged commit d6db22a into main Jan 29, 2025
19 checks passed
@freakboy3742 freakboy3742 deleted the py314-compat branch January 29, 2025 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test suite failing under Python 3.14.0a4

3 participants