Skip to content

Commit

Permalink
gh-127949: deprecate asyncio policy classes (#128216)
Browse files Browse the repository at this point in the history
  • Loading branch information
kumaraditya303 authored Dec 24, 2024
1 parent 3f6a618 commit a391d80
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 34 deletions.
16 changes: 16 additions & 0 deletions Doc/library/asyncio-policy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ The abstract event loop policy base class is defined as follows:

This method should never return ``None``.

.. deprecated:: next
The :class:`AbstractEventLoopPolicy` class is deprecated and
will be removed in Python 3.16.


.. _asyncio-policy-builtin:

Expand All @@ -109,6 +113,10 @@ asyncio ships with the following built-in policies:
The :meth:`get_event_loop` method of the default asyncio policy now
raises a :exc:`RuntimeError` if there is no set event loop.

.. deprecated:: next
The :class:`DefaultEventLoopPolicy` class is deprecated and
will be removed in Python 3.16.


.. class:: WindowsSelectorEventLoopPolicy

Expand All @@ -117,6 +125,10 @@ asyncio ships with the following built-in policies:

.. availability:: Windows.

.. deprecated:: next
The :class:`WindowsSelectorEventLoopPolicy` class is deprecated and
will be removed in Python 3.16.


.. class:: WindowsProactorEventLoopPolicy

Expand All @@ -125,6 +137,10 @@ asyncio ships with the following built-in policies:

.. availability:: Windows.

.. deprecated:: next
The :class:`WindowsProactorEventLoopPolicy` class is deprecated and
will be removed in Python 3.16.


.. _asyncio-custom-policies:

Expand Down
16 changes: 16 additions & 0 deletions Lib/asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,19 @@
else:
from .unix_events import * # pragma: no cover
__all__ += unix_events.__all__

def __getattr__(name: str):
import warnings

deprecated = {
"AbstractEventLoopPolicy",
"DefaultEventLoopPolicy",
"WindowsSelectorEventLoopPolicy",
"WindowsProactorEventLoopPolicy",
}
if name in deprecated:
warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
# deprecated things have underscores in front of them
return globals()["_" + name]

raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
14 changes: 7 additions & 7 deletions Lib/asyncio/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# SPDX-FileCopyrightText: Copyright (c) 2015-2021 MagicStack Inc. http://magic.io

__all__ = (
'AbstractEventLoopPolicy',
'_AbstractEventLoopPolicy',
'AbstractEventLoop', 'AbstractServer',
'Handle', 'TimerHandle',
'_get_event_loop_policy',
Expand Down Expand Up @@ -632,7 +632,7 @@ def set_debug(self, enabled):
raise NotImplementedError


class AbstractEventLoopPolicy:
class _AbstractEventLoopPolicy:
"""Abstract policy for accessing the event loop."""

def get_event_loop(self):
Expand All @@ -655,7 +655,7 @@ def new_event_loop(self):
the current context, set_event_loop must be called explicitly."""
raise NotImplementedError

class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
class _BaseDefaultEventLoopPolicy(_AbstractEventLoopPolicy):
"""Default policy implementation for accessing the event loop.
In this policy, each thread has its own event loop. However, we
Expand Down Expand Up @@ -758,8 +758,8 @@ def _init_event_loop_policy():
global _event_loop_policy
with _lock:
if _event_loop_policy is None: # pragma: no branch
from . import DefaultEventLoopPolicy
_event_loop_policy = DefaultEventLoopPolicy()
from . import _DefaultEventLoopPolicy
_event_loop_policy = _DefaultEventLoopPolicy()


def _get_event_loop_policy():
Expand All @@ -777,7 +777,7 @@ def _set_event_loop_policy(policy):
If policy is None, the default policy is restored."""
global _event_loop_policy
if policy is not None and not isinstance(policy, AbstractEventLoopPolicy):
if policy is not None and not isinstance(policy, _AbstractEventLoopPolicy):
raise TypeError(f"policy must be an instance of AbstractEventLoopPolicy or None, not '{type(policy).__name__}'")
_event_loop_policy = policy

Expand Down Expand Up @@ -838,7 +838,7 @@ def new_event_loop():
def on_fork():
# Reset the loop and wakeupfd in the forked child process.
if _event_loop_policy is not None:
_event_loop_policy._local = BaseDefaultEventLoopPolicy._Local()
_event_loop_policy._local = _BaseDefaultEventLoopPolicy._Local()
_set_running_loop(None)
signal.set_wakeup_fd(-1)

Expand Down
6 changes: 3 additions & 3 deletions Lib/asyncio/unix_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

__all__ = (
'SelectorEventLoop',
'DefaultEventLoopPolicy',
'_DefaultEventLoopPolicy',
'EventLoop',
)

Expand Down Expand Up @@ -963,11 +963,11 @@ def can_use_pidfd():
return True


class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
class _UnixDefaultEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
"""UNIX event loop policy"""
_loop_factory = _UnixSelectorEventLoop


SelectorEventLoop = _UnixSelectorEventLoop
DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
_DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
EventLoop = SelectorEventLoop
10 changes: 5 additions & 5 deletions Lib/asyncio/windows_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@

__all__ = (
'SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor',
'DefaultEventLoopPolicy', 'WindowsSelectorEventLoopPolicy',
'WindowsProactorEventLoopPolicy', 'EventLoop',
'_DefaultEventLoopPolicy', '_WindowsSelectorEventLoopPolicy',
'_WindowsProactorEventLoopPolicy', 'EventLoop',
)


Expand Down Expand Up @@ -891,13 +891,13 @@ def callback(f):
SelectorEventLoop = _WindowsSelectorEventLoop


class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
class _WindowsSelectorEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
_loop_factory = SelectorEventLoop


class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
class _WindowsProactorEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
_loop_factory = ProactorEventLoop


DefaultEventLoopPolicy = WindowsProactorEventLoopPolicy
_DefaultEventLoopPolicy = _WindowsProactorEventLoopPolicy
EventLoop = ProactorEventLoop
34 changes: 23 additions & 11 deletions Lib/test/test_asyncio/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2695,22 +2695,34 @@ async def inner():

class PolicyTests(unittest.TestCase):

def test_abstract_event_loop_policy_deprecation(self):
with self.assertWarnsRegex(
DeprecationWarning, "'asyncio.AbstractEventLoopPolicy' is deprecated"):
policy = asyncio.AbstractEventLoopPolicy()
self.assertIsInstance(policy, asyncio.AbstractEventLoopPolicy)

def test_default_event_loop_policy_deprecation(self):
with self.assertWarnsRegex(
DeprecationWarning, "'asyncio.DefaultEventLoopPolicy' is deprecated"):
policy = asyncio.DefaultEventLoopPolicy()
self.assertIsInstance(policy, asyncio.DefaultEventLoopPolicy)

def test_event_loop_policy(self):
policy = asyncio.AbstractEventLoopPolicy()
policy = asyncio._AbstractEventLoopPolicy()
self.assertRaises(NotImplementedError, policy.get_event_loop)
self.assertRaises(NotImplementedError, policy.set_event_loop, object())
self.assertRaises(NotImplementedError, policy.new_event_loop)

def test_get_event_loop(self):
policy = asyncio.DefaultEventLoopPolicy()
policy = asyncio._DefaultEventLoopPolicy()
self.assertIsNone(policy._local._loop)

with self.assertRaises(RuntimeError):
loop = policy.get_event_loop()
self.assertIsNone(policy._local._loop)

def test_get_event_loop_does_not_call_set_event_loop(self):
policy = asyncio.DefaultEventLoopPolicy()
policy = asyncio._DefaultEventLoopPolicy()

with mock.patch.object(
policy, "set_event_loop",
Expand All @@ -2722,30 +2734,30 @@ def test_get_event_loop_does_not_call_set_event_loop(self):
m_set_event_loop.assert_not_called()

def test_get_event_loop_after_set_none(self):
policy = asyncio.DefaultEventLoopPolicy()
policy = asyncio._DefaultEventLoopPolicy()
policy.set_event_loop(None)
self.assertRaises(RuntimeError, policy.get_event_loop)

@mock.patch('asyncio.events.threading.current_thread')
def test_get_event_loop_thread(self, m_current_thread):

def f():
policy = asyncio.DefaultEventLoopPolicy()
policy = asyncio._DefaultEventLoopPolicy()
self.assertRaises(RuntimeError, policy.get_event_loop)

th = threading.Thread(target=f)
th.start()
th.join()

def test_new_event_loop(self):
policy = asyncio.DefaultEventLoopPolicy()
policy = asyncio._DefaultEventLoopPolicy()

loop = policy.new_event_loop()
self.assertIsInstance(loop, asyncio.AbstractEventLoop)
loop.close()

def test_set_event_loop(self):
policy = asyncio.DefaultEventLoopPolicy()
policy = asyncio._DefaultEventLoopPolicy()
old_loop = policy.new_event_loop()
policy.set_event_loop(old_loop)

Expand All @@ -2762,7 +2774,7 @@ def test_get_event_loop_policy(self):
with self.assertWarnsRegex(
DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"):
policy = asyncio.get_event_loop_policy()
self.assertIsInstance(policy, asyncio.AbstractEventLoopPolicy)
self.assertIsInstance(policy, asyncio._AbstractEventLoopPolicy)
self.assertIs(policy, asyncio.get_event_loop_policy())

def test_set_event_loop_policy(self):
Expand All @@ -2775,7 +2787,7 @@ def test_set_event_loop_policy(self):
DeprecationWarning, "'asyncio.get_event_loop_policy' is deprecated"):
old_policy = asyncio.get_event_loop_policy()

policy = asyncio.DefaultEventLoopPolicy()
policy = asyncio._DefaultEventLoopPolicy()
with self.assertWarnsRegex(
DeprecationWarning, "'asyncio.set_event_loop_policy' is deprecated"):
asyncio.set_event_loop_policy(policy)
Expand Down Expand Up @@ -2862,7 +2874,7 @@ def test_get_event_loop_returns_running_loop(self):
class TestError(Exception):
pass

class Policy(asyncio.DefaultEventLoopPolicy):
class Policy(asyncio._DefaultEventLoopPolicy):
def get_event_loop(self):
raise TestError

Expand Down Expand Up @@ -2908,7 +2920,7 @@ async def func():
def test_get_event_loop_returns_running_loop2(self):
old_policy = asyncio._get_event_loop_policy()
try:
asyncio._set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
asyncio._set_event_loop_policy(asyncio._DefaultEventLoopPolicy())
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_asyncio/test_runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def interrupt_self():
_thread.interrupt_main()


class TestPolicy(asyncio.AbstractEventLoopPolicy):
class TestPolicy(asyncio._AbstractEventLoopPolicy):

def __init__(self, loop_factory):
self.loop_factory = loop_factory
Expand Down
18 changes: 11 additions & 7 deletions Lib/test/test_asyncio/test_windows_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,14 +328,15 @@ class WinPolicyTests(WindowsEventsTestCase):

def test_selector_win_policy(self):
async def main():
self.assertIsInstance(
asyncio.get_running_loop(),
asyncio.SelectorEventLoop)
self.assertIsInstance(asyncio.get_running_loop(), asyncio.SelectorEventLoop)

old_policy = asyncio._get_event_loop_policy()
try:
asyncio._set_event_loop_policy(
asyncio.WindowsSelectorEventLoopPolicy())
with self.assertWarnsRegex(
DeprecationWarning,
"'asyncio.WindowsSelectorEventLoopPolicy' is deprecated",
):
asyncio._set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(main())
finally:
asyncio._set_event_loop_policy(old_policy)
Expand All @@ -348,8 +349,11 @@ async def main():

old_policy = asyncio._get_event_loop_policy()
try:
asyncio._set_event_loop_policy(
asyncio.WindowsProactorEventLoopPolicy())
with self.assertWarnsRegex(
DeprecationWarning,
"'asyncio.WindowsProactorEventLoopPolicy' is deprecated",
):
asyncio._set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
asyncio.run(main())
finally:
asyncio._set_event_loop_policy(old_policy)
Expand Down

0 comments on commit a391d80

Please sign in to comment.