https://github.com/python/cpython/commit/b3154aa4507f6d2612c128e912860be963dbff1a
commit: b3154aa4507f6d2612c128e912860be963dbff1a
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-06-27T12:46:50+05:30
summary:

gh-127949: remove asyncio policy system (#150310)

files:
A Misc/NEWS.d/next/Library/2026-05-19-11-42-37.gh-issue-127949.Ab3kZq.rst
D Doc/library/asyncio-policy.rst
M Doc/deprecations/pending-removal-in-3.16.rst
M Doc/library/asyncio-eventloop.rst
M Doc/library/asyncio-llapi-index.rst
M Doc/library/asyncio-runner.rst
M Doc/library/asyncio.rst
M Doc/tools/check-html-ids.py
M Doc/tools/removed-ids.txt
M Doc/whatsnew/3.14.rst
M Doc/whatsnew/3.16.rst
M Include/internal/pycore_global_objects_fini_generated.h
M Include/internal/pycore_global_strings.h
M Include/internal/pycore_runtime_init_generated.h
M Include/internal/pycore_unicodeobject_generated.h
M Lib/asyncio/__init__.py
M Lib/asyncio/events.py
M Lib/asyncio/unix_events.py
M Lib/asyncio/windows_events.py
M Lib/test/libregrtest/save_env.py
M Lib/test/support/__init__.py
M Lib/test/test_asyncgen.py
M Lib/test/test_asyncio/test_base_events.py
M Lib/test/test_asyncio/test_buffered_proto.py
M Lib/test/test_asyncio/test_context.py
M Lib/test/test_asyncio/test_eager_task_factory.py
M Lib/test/test_asyncio/test_events.py
M Lib/test/test_asyncio/test_free_threading.py
M Lib/test/test_asyncio/test_futures.py
M Lib/test/test_asyncio/test_futures2.py
M Lib/test/test_asyncio/test_graph.py
M Lib/test/test_asyncio/test_locks.py
M Lib/test/test_asyncio/test_pep492.py
M Lib/test/test_asyncio/test_proactor_events.py
M Lib/test/test_asyncio/test_protocols.py
M Lib/test/test_asyncio/test_queues.py
M Lib/test/test_asyncio/test_runners.py
M Lib/test/test_asyncio/test_selector_events.py
M Lib/test/test_asyncio/test_sendfile.py
M Lib/test/test_asyncio/test_server.py
M Lib/test/test_asyncio/test_server_context.py
M Lib/test/test_asyncio/test_sock_lowlevel.py
M Lib/test/test_asyncio/test_ssl.py
M Lib/test/test_asyncio/test_sslproto.py
M Lib/test/test_asyncio/test_staggered.py
M Lib/test/test_asyncio/test_streams.py
M Lib/test/test_asyncio/test_subprocess.py
M Lib/test/test_asyncio/test_taskgroups.py
M Lib/test/test_asyncio/test_tasks.py
M Lib/test/test_asyncio/test_threads.py
M Lib/test/test_asyncio/test_timeouts.py
M Lib/test/test_asyncio/test_transports.py
M Lib/test/test_asyncio/test_unix_events.py
M Lib/test/test_asyncio/test_waitfor.py
M Lib/test/test_asyncio/test_windows_events.py
M Lib/test/test_asyncio/test_windows_utils.py
M Lib/test/test_asyncio/utils.py
M Lib/test/test_concurrent_futures/test_interpreter_pool.py
M Lib/test/test_coroutines.py
M Lib/test/test_inspect/test_inspect.py
M Lib/test/test_logging.py
M Lib/test/test_os/test_os.py
M Lib/test/test_pdb.py
M Lib/test/test_unittest/test_async_case.py
M Lib/test/test_unittest/testmock/testasync.py
M Lib/unittest/async_case.py
M Modules/_asynciomodule.c
M Modules/clinic/_asynciomodule.c.h

diff --git a/Doc/deprecations/pending-removal-in-3.16.rst 
b/Doc/deprecations/pending-removal-in-3.16.rst
index 5a28cc766a95961..9b19ec3236f92fb 100644
--- a/Doc/deprecations/pending-removal-in-3.16.rst
+++ b/Doc/deprecations/pending-removal-in-3.16.rst
@@ -33,12 +33,12 @@ Pending removal in Python 3.16
   * :mod:`asyncio` policy system is deprecated and will be removed in Python 
3.16.
     In particular, the following classes and functions are deprecated:
 
-    * :class:`asyncio.AbstractEventLoopPolicy`
-    * :class:`asyncio.DefaultEventLoopPolicy`
-    * :class:`asyncio.WindowsSelectorEventLoopPolicy`
-    * :class:`asyncio.WindowsProactorEventLoopPolicy`
-    * :func:`asyncio.get_event_loop_policy`
-    * :func:`asyncio.set_event_loop_policy`
+    * :class:`!asyncio.AbstractEventLoopPolicy`
+    * :class:`!asyncio.DefaultEventLoopPolicy`
+    * :class:`!asyncio.WindowsSelectorEventLoopPolicy`
+    * :class:`!asyncio.WindowsProactorEventLoopPolicy`
+    * :func:`!asyncio.get_event_loop_policy`
+    * :func:`!asyncio.set_event_loop_policy`
 
     Users should use :func:`asyncio.run` or :class:`asyncio.Runner` with
     *loop_factory* to use the desired event loop implementation.
diff --git a/Doc/library/asyncio-eventloop.rst 
b/Doc/library/asyncio-eventloop.rst
index 79c9516cda2d60b..65bc349e2a727d3 100644
--- a/Doc/library/asyncio-eventloop.rst
+++ b/Doc/library/asyncio-eventloop.rst
@@ -48,10 +48,10 @@ an event loop:
    running event loop.
 
    If there is no running event loop set, the function will return
-   the result of the ``get_event_loop_policy().get_event_loop()`` call.
+   the loop set by :func:`set_event_loop`, or raise a :exc:`RuntimeError`
+   if no loop has been set.
 
-   Because this function has rather complex behavior (especially
-   when custom event loop policies are in use), using the
+   Because this function has rather complex behavior, using the
    :func:`get_running_loop` function is preferred to :func:`get_event_loop`
    in coroutines and callbacks.
 
@@ -62,13 +62,6 @@ an event loop:
    .. versionchanged:: 3.14
       Raises a :exc:`RuntimeError` if there is no current event loop.
 
-   .. note::
-
-      The :mod:`!asyncio` policy system is deprecated and will be removed
-      in Python 3.16; from there on, this function will return the current
-      running event loop if present else it will return the
-      loop set by :func:`set_event_loop`.
-
 .. function:: set_event_loop(loop)
 
    Set *loop* as the current event loop for the current OS thread.
@@ -77,10 +70,6 @@ an event loop:
 
    Create and return a new event loop object.
 
-Note that the behaviour of :func:`get_event_loop`, :func:`set_event_loop`,
-and :func:`new_event_loop` functions can be altered by
-:ref:`setting a custom event loop policy <asyncio-policies>`.
-
 
 .. rubric:: Contents
 
diff --git a/Doc/library/asyncio-llapi-index.rst 
b/Doc/library/asyncio-llapi-index.rst
index f5af888f31f1863..faf67209a2b0ef5 100644
--- a/Doc/library/asyncio-llapi-index.rst
+++ b/Doc/library/asyncio-llapi-index.rst
@@ -19,10 +19,10 @@ Obtaining the Event Loop
       - The **preferred** function to get the running event loop.
 
     * - :func:`asyncio.get_event_loop`
-      - Get an event loop instance (running or current via the current policy).
+      - Get the running event loop or the event loop set for the current 
thread.
 
     * - :func:`asyncio.set_event_loop`
-      - Set the event loop as current via the current policy.
+      - Set the event loop for the current thread.
 
     * - :func:`asyncio.new_event_loop`
       - Create a new event loop.
@@ -497,27 +497,3 @@ Protocol classes can implement the following **callback 
methods**:
       - Called when the child process has exited. It can be called before
         :meth:`~SubprocessProtocol.pipe_data_received` and
         :meth:`~SubprocessProtocol.pipe_connection_lost` methods.
-
-
-Event Loop Policies
-===================
-
-Policies is a low-level mechanism to alter the behavior of
-functions like :func:`asyncio.get_event_loop`.  See also
-the main :ref:`policies section <asyncio-policies>` for more
-details.
-
-
-.. rubric:: Accessing Policies
-.. list-table::
-    :widths: 50 50
-    :class: full-width-table
-
-    * - :meth:`asyncio.get_event_loop_policy`
-      - Return the current process-wide policy.
-
-    * - :meth:`asyncio.set_event_loop_policy`
-      - Set a new process-wide policy.
-
-    * - :class:`AbstractEventLoopPolicy`
-      - Base class for policy objects.
diff --git a/Doc/library/asyncio-policy.rst b/Doc/library/asyncio-policy.rst
deleted file mode 100644
index 57f964912dd6ea2..000000000000000
--- a/Doc/library/asyncio-policy.rst
+++ /dev/null
@@ -1,173 +0,0 @@
-.. currentmodule:: asyncio
-
-
-.. _asyncio-policies:
-
-========
-Policies
-========
-
-.. warning::
-
-   Policies are deprecated and will be removed in Python 3.16.
-   Users are encouraged to use the :func:`asyncio.run` function
-   or the :class:`asyncio.Runner` with *loop_factory* to use
-   the desired loop implementation.
-
-
-An event loop policy is a global object
-used to get and set the current :ref:`event loop <asyncio-event-loop>`,
-as well as create new event loops.
-The default policy can be :ref:`replaced <asyncio-policy-get-set>` with
-:ref:`built-in alternatives <asyncio-policy-builtin>`
-to use different event loop implementations,
-or substituted by a :ref:`custom policy <asyncio-custom-policies>`
-that can override these behaviors.
-
-The :ref:`policy object <asyncio-policy-objects>`
-gets and sets a separate event loop per *context*.
-This is per-thread by default,
-though custom policies could define *context* differently.
-
-Custom event loop policies can control the behavior of
-:func:`get_event_loop`, :func:`set_event_loop`, and :func:`new_event_loop`.
-
-Policy objects should implement the APIs defined
-in the :class:`AbstractEventLoopPolicy` abstract base class.
-
-
-.. _asyncio-policy-get-set:
-
-Getting and Setting the Policy
-==============================
-
-The following functions can be used to get and set the policy
-for the current process:
-
-.. function:: get_event_loop_policy()
-
-   Return the current process-wide policy.
-
-   .. deprecated:: 3.14
-      The :func:`get_event_loop_policy` function is deprecated and
-      will be removed in Python 3.16.
-
-.. function:: set_event_loop_policy(policy)
-
-   Set the current process-wide policy to *policy*.
-
-   If *policy* is set to ``None``, the default policy is restored.
-
-   .. deprecated:: 3.14
-      The :func:`set_event_loop_policy` function is deprecated and
-      will be removed in Python 3.16.
-
-
-.. _asyncio-policy-objects:
-
-Policy Objects
-==============
-
-The abstract event loop policy base class is defined as follows:
-
-.. class:: AbstractEventLoopPolicy
-
-   An abstract base class for asyncio policies.
-
-   .. method:: get_event_loop()
-
-      Get the event loop for the current context.
-
-      Return an event loop object implementing the
-      :class:`AbstractEventLoop` interface.
-
-      This method should never return ``None``.
-
-      .. versionchanged:: 3.6
-
-   .. method:: set_event_loop(loop)
-
-      Set the event loop for the current context to *loop*.
-
-   .. method:: new_event_loop()
-
-      Create and return a new event loop object.
-
-      This method should never return ``None``.
-
-   .. deprecated:: 3.14
-      The :class:`AbstractEventLoopPolicy` class is deprecated and
-      will be removed in Python 3.16.
-
-
-.. _asyncio-policy-builtin:
-
-asyncio ships with the following built-in policies:
-
-
-.. class:: DefaultEventLoopPolicy
-
-   The default asyncio policy.  Uses :class:`SelectorEventLoop`
-   on Unix and :class:`ProactorEventLoop` on Windows.
-
-   There is no need to install the default policy manually. asyncio
-   is configured to use the default policy automatically.
-
-   .. versionchanged:: 3.8
-
-      On Windows, :class:`ProactorEventLoop` is now used by default.
-
-   .. versionchanged:: 3.14
-      The :meth:`get_event_loop` method of the default asyncio policy now
-      raises a :exc:`RuntimeError` if there is no set event loop.
-
-   .. deprecated:: 3.14
-      The :class:`DefaultEventLoopPolicy` class is deprecated and
-      will be removed in Python 3.16.
-
-
-.. class:: WindowsSelectorEventLoopPolicy
-
-   An alternative event loop policy that uses the
-   :class:`SelectorEventLoop` event loop implementation.
-
-   .. availability:: Windows.
-
-   .. deprecated:: 3.14
-      The :class:`WindowsSelectorEventLoopPolicy` class is deprecated and
-      will be removed in Python 3.16.
-
-
-.. class:: WindowsProactorEventLoopPolicy
-
-   An alternative event loop policy that uses the
-   :class:`ProactorEventLoop` event loop implementation.
-
-   .. availability:: Windows.
-
-   .. deprecated:: 3.14
-      The :class:`WindowsProactorEventLoopPolicy` class is deprecated and
-      will be removed in Python 3.16.
-
-
-.. _asyncio-custom-policies:
-
-Custom Policies
-===============
-
-To implement a new event loop policy, it is recommended to subclass
-:class:`DefaultEventLoopPolicy` and override the methods for which
-custom behavior is wanted, e.g.::
-
-    class MyEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
-
-        def get_event_loop(self):
-            """Get the event loop.
-
-            This may be None or an instance of EventLoop.
-            """
-            loop = super().get_event_loop()
-            # Do something with loop ...
-            return loop
-
-    asyncio.set_event_loop_policy(MyEventLoopPolicy())
diff --git a/Doc/library/asyncio-runner.rst b/Doc/library/asyncio-runner.rst
index 48d78099fd3ce7e..cd125b30703c796 100644
--- a/Doc/library/asyncio-runner.rst
+++ b/Doc/library/asyncio-runner.rst
@@ -43,9 +43,7 @@ Running an asyncio Program
    otherwise :func:`asyncio.new_event_loop` is used. The loop is closed at the 
end.
    This function should be used as a main entry point for asyncio programs,
    and should ideally only be called once. It is recommended to use
-   *loop_factory* to configure the event loop instead of policies.
-   Passing :class:`asyncio.EventLoop` allows running asyncio without the
-   policy system.
+   *loop_factory* to configure the event loop.
 
    The executor is given a timeout duration of 5 minutes to shutdown.
    If the executor hasn't finished within that duration, a warning is
@@ -76,12 +74,6 @@ Running an asyncio Program
 
       *coro* can be any awaitable object.
 
-   .. note::
-
-      The :mod:`!asyncio` policy system is deprecated and will be removed
-      in Python 3.16; from there on, an explicit *loop_factory* is needed
-      to configure the event loop.
-
 
 Runner context manager
 ======================
diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst
index 90a465f3e1d3af4..4ae6d1e43f2ac7b 100644
--- a/Doc/library/asyncio.rst
+++ b/Doc/library/asyncio.rst
@@ -117,7 +117,6 @@ for full functionality and the latest features.
    asyncio-eventloop.rst
    asyncio-future.rst
    asyncio-protocol.rst
-   asyncio-policy.rst
    asyncio-platforms.rst
    asyncio-extending.rst
 
diff --git a/Doc/tools/check-html-ids.py b/Doc/tools/check-html-ids.py
index 7d86c6cc3264ad5..3ea0a99d1dd4f68 100644
--- a/Doc/tools/check-html-ids.py
+++ b/Doc/tools/check-html-ids.py
@@ -78,9 +78,10 @@ def do_check(baseline, checked, excluded, *, verbose_print):
         try:
             checked_ids = checked[name]
         except KeyError:
-            successful = False
-            print(f'{name}: (page missing)')
-            print()
+            if (name, '(page missing)') not in excluded:
+                successful = False
+                print(f'{name}: (page missing)')
+                print()
         else:
             missing_ids = set(baseline_ids) - set(checked_ids)
             if missing_ids:
diff --git a/Doc/tools/removed-ids.txt b/Doc/tools/removed-ids.txt
index 271b383ba812307..0549ae735a92d63 100644
--- a/Doc/tools/removed-ids.txt
+++ b/Doc/tools/removed-ids.txt
@@ -6,6 +6,7 @@ c-api/file.html: deprecated-api
 
 # Removed sections
 library/asyncio-task.html: terminating-a-task-group
+library/asyncio-llapi-index.html: event-loop-policies
 deprecations/index.html: pending-removal-in-python-3-15
 deprecations/index.html: pending-removal-in-python-3-16
 deprecations/index.html: c-api-pending-removal-in-python-3-15
@@ -35,3 +36,6 @@ reference/expressions.html: 
grammar-token-python-grammar-set_display
 # Moved to a different page
 c-api/typeobj.html: c.Py_tp_base
 c-api/typeobj.html: c.Py_tp_bases
+
+# Removed pages
+library/asyncio-policy.html: (page missing)
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index cd0d8b7cb006fee..2fa7267292d9940 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -2628,12 +2628,12 @@ New deprecations
     and will be removed in Python 3.16.
     In particular, the following classes and functions are deprecated:
 
-    * :class:`asyncio.AbstractEventLoopPolicy`
-    * :class:`asyncio.DefaultEventLoopPolicy`
-    * :class:`asyncio.WindowsSelectorEventLoopPolicy`
-    * :class:`asyncio.WindowsProactorEventLoopPolicy`
-    * :func:`asyncio.get_event_loop_policy`
-    * :func:`asyncio.set_event_loop_policy`
+    * :class:`!asyncio.AbstractEventLoopPolicy`
+    * :class:`!asyncio.DefaultEventLoopPolicy`
+    * :class:`!asyncio.WindowsSelectorEventLoopPolicy`
+    * :class:`!asyncio.WindowsProactorEventLoopPolicy`
+    * :func:`!asyncio.get_event_loop_policy`
+    * :func:`!asyncio.set_event_loop_policy`
 
     Users should use :func:`asyncio.run` or :class:`asyncio.Runner` with
     the *loop_factory* argument to use the desired event loop implementation.
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index e29d6e9b9e25ee9..7841fa56cc5952c 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -411,6 +411,17 @@ asyncio
   which has been deprecated since Python 3.14.
   Use :func:`inspect.iscoroutinefunction` instead.
 
+* The event loop policy system, including the
+  :class:`!asyncio.AbstractEventLoopPolicy`,
+  :class:`!asyncio.DefaultEventLoopPolicy`,
+  :class:`!asyncio.WindowsSelectorEventLoopPolicy` and
+  :class:`!asyncio.WindowsProactorEventLoopPolicy` classes and the
+  :func:`!asyncio.get_event_loop_policy` and
+  :func:`!asyncio.set_event_loop_policy` functions,
+  which have been deprecated since Python 3.14.
+  Use :func:`asyncio.run` or :class:`asyncio.Runner` with a custom
+  *loop_factory* instead.
+
 functools
 ---------
 
diff --git a/Include/internal/pycore_global_objects_fini_generated.h 
b/Include/internal/pycore_global_objects_fini_generated.h
index 87dde5e062bd724..7d952a1e52561a6 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -1794,7 +1794,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(generation));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(get));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(get_debug));
-    _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(get_event_loop));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(get_loop));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(get_source));
     _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(getattr));
diff --git a/Include/internal/pycore_global_strings.h 
b/Include/internal/pycore_global_strings.h
index 43286fa36bbb05c..8a8bbc3b6d05bf7 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -517,7 +517,6 @@ struct _Py_global_strings {
         STRUCT_FOR_ID(generation)
         STRUCT_FOR_ID(get)
         STRUCT_FOR_ID(get_debug)
-        STRUCT_FOR_ID(get_event_loop)
         STRUCT_FOR_ID(get_loop)
         STRUCT_FOR_ID(get_source)
         STRUCT_FOR_ID(getattr)
diff --git a/Include/internal/pycore_runtime_init_generated.h 
b/Include/internal/pycore_runtime_init_generated.h
index 116dbf84f790124..366d2d300fb4780 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -1792,7 +1792,6 @@ extern "C" {
     INIT_ID(generation), \
     INIT_ID(get), \
     INIT_ID(get_debug), \
-    INIT_ID(get_event_loop), \
     INIT_ID(get_loop), \
     INIT_ID(get_source), \
     INIT_ID(getattr), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h 
b/Include/internal/pycore_unicodeobject_generated.h
index f7197b39d9bd3ab..00d6297432b5fca 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -1848,10 +1848,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) 
{
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
     assert(PyUnicode_GET_LENGTH(string) != 1);
-    string = &_Py_ID(get_event_loop);
-    _PyUnicode_InternStatic(interp, &string);
-    assert(_PyUnicode_CheckConsistency(string, 1));
-    assert(PyUnicode_GET_LENGTH(string) != 1);
     string = &_Py_ID(get_loop);
     _PyUnicode_InternStatic(interp, &string);
     assert(_PyUnicode_CheckConsistency(string, 1));
diff --git a/Lib/asyncio/__init__.py b/Lib/asyncio/__init__.py
index 32a5dbae03af216..2432e2dad74c23d 100644
--- a/Lib/asyncio/__init__.py
+++ b/Lib/asyncio/__init__.py
@@ -47,28 +47,3 @@
 else:
     from .unix_events import *  # pragma: no cover
     __all__ += unix_events.__all__
-
-def __getattr__(name: str):
-    import warnings
-
-    match name:
-        case "AbstractEventLoopPolicy":
-            warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
-            return events._AbstractEventLoopPolicy
-        case "DefaultEventLoopPolicy":
-            warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
-            if sys.platform == 'win32':
-                return windows_events._DefaultEventLoopPolicy
-            return unix_events._DefaultEventLoopPolicy
-        case "WindowsSelectorEventLoopPolicy":
-            if sys.platform == 'win32':
-                warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
-                return windows_events._WindowsSelectorEventLoopPolicy
-            # Else fall through to the AttributeError below.
-        case "WindowsProactorEventLoopPolicy":
-            if sys.platform == 'win32':
-                warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
-                return windows_events._WindowsProactorEventLoopPolicy
-            # Else fall through to the AttributeError below.
-
-    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py
index 42d8408a38dfd1f..807c70bc775aa2c 100644
--- a/Lib/asyncio/events.py
+++ b/Lib/asyncio/events.py
@@ -9,8 +9,6 @@
     "AbstractServer",
     "Handle",
     "TimerHandle",
-    "get_event_loop_policy",
-    "set_event_loop_policy",
     "get_event_loop",
     "set_event_loop",
     "new_event_loop",
@@ -26,7 +24,6 @@
 import subprocess
 import sys
 import threading
-import warnings
 
 from . import format_helpers
 
@@ -665,84 +662,11 @@ def set_debug(self, enabled):
         raise NotImplementedError
 
 
-class _AbstractEventLoopPolicy:
-    """Abstract policy for accessing the event loop."""
+class _Local(threading.local):
+    _loop = None
 
-    def get_event_loop(self):
-        """Get the event loop for the current context.
 
-        Returns an event loop object implementing the AbstractEventLoop 
interface,
-        or raises an exception in case no event loop has been set for the
-        current context and the current policy does not specify to create one.
-
-        It should never return None."""
-        raise NotImplementedError
-
-    def set_event_loop(self, loop):
-        """Set the event loop for the current context to loop."""
-        raise NotImplementedError
-
-    def new_event_loop(self):
-        """Create and return a new event loop object according to this
-        policy's rules. If there's need to set this loop as the event loop for
-        the current context, set_event_loop must be called explicitly."""
-        raise NotImplementedError
-
-class _BaseDefaultEventLoopPolicy(_AbstractEventLoopPolicy):
-    """Default policy implementation for accessing the event loop.
-
-    In this policy, each thread has its own event loop.  However, we
-    only automatically create an event loop by default for the main
-    thread; other threads by default have no event loop.
-
-    Other policies may have different rules (e.g. a single global
-    event loop, or automatically creating an event loop per thread, or
-    using some other notion of context to which an event loop is
-    associated).
-    """
-
-    _loop_factory = None
-
-    class _Local(threading.local):
-        _loop = None
-
-    def __init__(self):
-        self._local = self._Local()
-
-    def get_event_loop(self):
-        """Get the event loop for the current context.
-
-        Returns an instance of EventLoop or raises an exception.
-        """
-        if self._local._loop is None:
-            raise RuntimeError('There is no current event loop in thread %r.'
-                               % threading.current_thread().name)
-
-        return self._local._loop
-
-    def set_event_loop(self, loop):
-        """Set the event loop."""
-        if loop is not None and not isinstance(loop, AbstractEventLoop):
-            raise TypeError(f"loop must be an instance of AbstractEventLoop or 
None, not '{type(loop).__name__}'")
-        self._local._loop = loop
-
-    def new_event_loop(self):
-        """Create a new event loop.
-
-        You must call set_event_loop() to make this the current event
-        loop.
-        """
-        return self._loop_factory()
-
-
-# Event loop policy.  The policy itself is always global, even if the
-# policy's rules say that there is an event loop per thread (or other
-# notion of context).  The default policy is installed by the first
-# call to get_event_loop_policy().
-_event_loop_policy = None
-
-# Lock for protecting the on-the-fly creation of the event loop policy.
-_lock = threading.Lock()
+_local = _Local()
 
 
 # A TLS for the running event loop, used by _get_running_loop.
@@ -787,39 +711,19 @@ def _set_running_loop(loop):
     _running_loop.loop_pid = (loop, os.getpid())
 
 
-def _init_event_loop_policy():
-    global _event_loop_policy
-    with _lock:
-        if _event_loop_policy is None:  # pragma: no branch
-            if sys.platform == 'win32':
-                from .windows_events import _DefaultEventLoopPolicy
-            else:
-                from .unix_events import _DefaultEventLoopPolicy
-            _event_loop_policy = _DefaultEventLoopPolicy()
-
+def _get_event_loop():
+    """Return the event loop set for the current thread.
 
-def _get_event_loop_policy():
-    """Get the current event loop policy."""
-    if _event_loop_policy is None:
-        _init_event_loop_policy()
-    return _event_loop_policy
-
-def get_event_loop_policy():
-    warnings._deprecated('asyncio.get_event_loop_policy', remove=(3, 16))
-    return _get_event_loop_policy()
-
-def _set_event_loop_policy(policy):
-    """Set the current event loop policy.
-
-    If policy is None, the default policy is restored."""
-    global _event_loop_policy
-    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
+    Raise a RuntimeError if no event loop has been set for the current
+    thread.  This is the slow path of get_event_loop(); the running loop
+    is checked by the caller.
+    """
+    loop = _local._loop
+    if loop is None:
+        raise RuntimeError('There is no current event loop in thread %r.'
+                            % threading.current_thread().name)
+    return loop
 
-def set_event_loop_policy(policy):
-    warnings._deprecated('asyncio.set_event_loop_policy', remove=(3,16))
-    _set_event_loop_policy(policy)
 
 def get_event_loop():
     """Return an asyncio event loop.
@@ -828,23 +732,33 @@ def get_event_loop():
     or similar API), this function will always return the running event loop.
 
     If there is no running event loop set, the function will return
-    the result of `get_event_loop_policy().get_event_loop()` call.
+    the loop set by ``set_event_loop()``, or raise a RuntimeError if
+    no loop has been set.
     """
     # NOTE: this function is implemented in C (see _asynciomodule.c)
     current_loop = _get_running_loop()
     if current_loop is not None:
         return current_loop
-    return _get_event_loop_policy().get_event_loop()
+    return _get_event_loop()
 
 
 def set_event_loop(loop):
-    """Equivalent to calling get_event_loop_policy().set_event_loop(loop)."""
-    _get_event_loop_policy().set_event_loop(loop)
+    """Set the event loop for the current thread to loop.
+
+    If loop is None, the current event loop is unset.
+    """
+    if loop is not None and not isinstance(loop, AbstractEventLoop):
+        raise TypeError(f"loop must be an instance of AbstractEventLoop or 
None, not '{type(loop).__name__}'")
+    _local._loop = loop
 
 
 def new_event_loop():
-    """Equivalent to calling get_event_loop_policy().new_event_loop()."""
-    return _get_event_loop_policy().new_event_loop()
+    """Create and return a new event loop object."""
+    if sys.platform == 'win32':
+        from .windows_events import EventLoop
+    else:
+        from .unix_events import EventLoop
+    return EventLoop()
 
 
 # Alias pure-Python implementations for testing purposes.
@@ -873,8 +787,8 @@ def new_event_loop():
 if hasattr(os, 'fork'):
     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()
+        global _local
+        _local = _Local()
         _set_running_loop(None)
         signal.set_wakeup_fd(-1)
 
diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py
index 676e0b670faa49b..93dd8994e1bcd51 100644
--- a/Lib/asyncio/unix_events.py
+++ b/Lib/asyncio/unix_events.py
@@ -965,11 +965,5 @@ def can_use_pidfd():
     return True
 
 
-class _UnixDefaultEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
-    """UNIX event loop policy"""
-    _loop_factory = _UnixSelectorEventLoop
-
-
 SelectorEventLoop = _UnixSelectorEventLoop
-_DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
 EventLoop = SelectorEventLoop
diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py
index 0bf7732136f1f8e..8241c10b32d105c 100644
--- a/Lib/asyncio/windows_events.py
+++ b/Lib/asyncio/windows_events.py
@@ -16,7 +16,6 @@
 import time
 import weakref
 
-from . import events
 from . import base_subprocess
 from . import futures
 from . import exceptions
@@ -29,8 +28,7 @@
 
 __all__ = (
     'SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor',
-    '_DefaultEventLoopPolicy', '_WindowsSelectorEventLoopPolicy',
-    '_WindowsProactorEventLoopPolicy', 'EventLoop',
+    'EventLoop',
 )
 
 
@@ -893,14 +891,4 @@ def callback(f):
 
 SelectorEventLoop = _WindowsSelectorEventLoop
 
-
-class _WindowsSelectorEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
-    _loop_factory = SelectorEventLoop
-
-
-class _WindowsProactorEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
-    _loop_factory = ProactorEventLoop
-
-
-_DefaultEventLoopPolicy = _WindowsProactorEventLoopPolicy
 EventLoop = ProactorEventLoop
diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py
index 138465012a252c5..6393036118010e6 100644
--- a/Lib/test/libregrtest/save_env.py
+++ b/Lib/test/libregrtest/save_env.py
@@ -70,7 +70,7 @@ def __init__(self, test_name, verbose, quiet, *, pgo):
                  'sysconfig._CONFIG_VARS', 'sysconfig._INSTALL_SCHEMES',
                  'files', 'locale', 'warnings.showwarning',
                  'shutil_archive_formats', 'shutil_unpack_formats',
-                 'asyncio.events._event_loop_policy',
+                 'asyncio.events._local._loop',
                  'urllib.requests._url_tempfiles', 'urllib.requests._opener',
                  'stty_echo',
                 )
@@ -100,12 +100,12 @@ def restore_urllib_requests__opener(self, opener):
         urllib_request = self.get_module('urllib.request')
         urllib_request._opener = opener
 
-    def get_asyncio_events__event_loop_policy(self):
+    def get_asyncio_events__local__loop(self):
         self.try_get_module('asyncio')
-        return support.maybe_get_event_loop_policy()
-    def restore_asyncio_events__event_loop_policy(self, policy):
+        return support.maybe_get_event_loop()
+    def restore_asyncio_events__local__loop(self, loop):
         asyncio = self.get_module('asyncio')
-        asyncio.events._set_event_loop_policy(policy)
+        asyncio.events._local._loop = loop
 
     def get_sys_argv(self):
         return id(sys.argv), sys.argv, sys.argv[:]
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index a3f8a733a081f62..d17d9a2ecf8d9b0 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -2289,10 +2289,10 @@ def __gt__(self, other):
 
 SMALLEST = _SMALLEST()
 
-def maybe_get_event_loop_policy():
-    """Return the global event loop policy if one is set, else return None."""
+def maybe_get_event_loop():
+    """Return the event loop set for the current thread, else return None."""
     import asyncio.events
-    return asyncio.events._event_loop_policy
+    return asyncio.events._local._loop
 
 # Helpers for testing hashing.
 NHASHBITS = sys.hash_info.width # number of bits in hash() result
diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py
index cd33878d6c74ba6..e9eecbc83415518 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -629,7 +629,7 @@ def setUp(self):
     def tearDown(self):
         self.loop.close()
         self.loop = None
-        asyncio.events._set_event_loop_policy(None)
+        asyncio.set_event_loop(None)
 
     def check_async_iterator_anext(self, ait_class):
         with self.subTest(anext="pure-Python"):
diff --git a/Lib/test/test_asyncio/test_base_events.py 
b/Lib/test/test_asyncio/test_base_events.py
index e59bc25668b4cba..fa3821e0783858c 100644
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -29,7 +29,7 @@ class CustomError(Exception):
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 def mock_socket_module():
diff --git a/Lib/test/test_asyncio/test_buffered_proto.py 
b/Lib/test/test_asyncio/test_buffered_proto.py
index 6d3edcc36f5c79b..8300620bc598403 100644
--- a/Lib/test/test_asyncio/test_buffered_proto.py
+++ b/Lib/test/test_asyncio/test_buffered_proto.py
@@ -5,7 +5,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class ReceiveStuffProto(asyncio.BufferedProtocol):
diff --git a/Lib/test/test_asyncio/test_context.py 
b/Lib/test/test_asyncio/test_context.py
index f85f39839cbd798..cd3981c7959a479 100644
--- a/Lib/test/test_asyncio/test_context.py
+++ b/Lib/test/test_asyncio/test_context.py
@@ -4,7 +4,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 @unittest.skipUnless(decimal.HAVE_CONTEXTVAR, "decimal is built with a 
thread-local context")
diff --git a/Lib/test/test_asyncio/test_eager_task_factory.py 
b/Lib/test/test_asyncio/test_eager_task_factory.py
index 0561b54a3f19c66..594a48baa32e522 100644
--- a/Lib/test/test_asyncio/test_eager_task_factory.py
+++ b/Lib/test/test_asyncio/test_eager_task_factory.py
@@ -13,7 +13,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class EagerTaskFactoryLoopTests:
diff --git a/Lib/test/test_asyncio/test_events.py 
b/Lib/test/test_asyncio/test_events.py
index 919d543b0329e9f..fed9bf67217762d 100644
--- a/Lib/test/test_asyncio/test_events.py
+++ b/Lib/test/test_asyncio/test_events.py
@@ -38,7 +38,7 @@
 from test.support import ALWAYS_EQ, LARGEST, SMALLEST
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 def broken_unix_getsockname():
@@ -2830,107 +2830,74 @@ 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.events._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 = test_utils.DefaultEventLoopPolicy()
-        self.assertIsNone(policy._local._loop)
+        old_loop = asyncio.events._local._loop
+        try:
+            asyncio.set_event_loop(None)
+            self.assertIsNone(asyncio.events._local._loop)
 
-        with self.assertRaises(RuntimeError):
-            loop = policy.get_event_loop()
-        self.assertIsNone(policy._local._loop)
+            with self.assertRaises(RuntimeError):
+                asyncio.get_event_loop()
+            self.assertIsNone(asyncio.events._local._loop)
+        finally:
+            asyncio.events._local._loop = old_loop
 
     def test_get_event_loop_does_not_call_set_event_loop(self):
-        policy = test_utils.DefaultEventLoopPolicy()
+        old_loop = asyncio.events._local._loop
+        try:
+            asyncio.set_event_loop(None)
 
-        with mock.patch.object(
-                policy, "set_event_loop",
-                wraps=policy.set_event_loop) as m_set_event_loop:
+            with mock.patch(
+                    'asyncio.events.set_event_loop',
+                    wraps=asyncio.events.set_event_loop) as m_set_event_loop:
 
-            with self.assertRaises(RuntimeError):
-                loop = policy.get_event_loop()
+                with self.assertRaises(RuntimeError):
+                    asyncio.get_event_loop()
+
+                m_set_event_loop.assert_not_called()
 
-            m_set_event_loop.assert_not_called()
+            self.assertIsNone(asyncio.events._local._loop)
+        finally:
+            asyncio.events._local._loop = old_loop
 
     def test_get_event_loop_after_set_none(self):
-        policy = test_utils.DefaultEventLoopPolicy()
-        policy.set_event_loop(None)
-        self.assertRaises(RuntimeError, policy.get_event_loop)
+        old_loop = asyncio.events._local._loop
+        try:
+            asyncio.set_event_loop(None)
+            self.assertRaises(RuntimeError, asyncio.get_event_loop)
+        finally:
+            asyncio.events._local._loop = old_loop
 
-    @mock.patch('asyncio.events.threading.current_thread')
-    def test_get_event_loop_thread(self, m_current_thread):
+    def test_get_event_loop_thread(self):
 
         def f():
-            policy = test_utils.DefaultEventLoopPolicy()
-            self.assertRaises(RuntimeError, policy.get_event_loop)
+            self.assertRaises(RuntimeError, asyncio.get_event_loop)
 
         th = threading.Thread(target=f)
         th.start()
         th.join()
 
     def test_new_event_loop(self):
-        policy = test_utils.DefaultEventLoopPolicy()
-
-        loop = policy.new_event_loop()
+        loop = asyncio.new_event_loop()
         self.assertIsInstance(loop, asyncio.AbstractEventLoop)
         loop.close()
 
     def test_set_event_loop(self):
-        policy = test_utils.DefaultEventLoopPolicy()
-        old_loop = policy.new_event_loop()
-        policy.set_event_loop(old_loop)
-
-        self.assertRaises(TypeError, policy.set_event_loop, object())
-
-        loop = policy.new_event_loop()
-        policy.set_event_loop(loop)
-        self.assertIs(loop, policy.get_event_loop())
-        self.assertIsNot(old_loop, policy.get_event_loop())
-        loop.close()
-        old_loop.close()
-
-    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.events._AbstractEventLoopPolicy)
-            self.assertIs(policy, asyncio.get_event_loop_policy())
-
-    def test_set_event_loop_policy(self):
-        with self.assertWarnsRegex(
-                DeprecationWarning, "'asyncio.set_event_loop_policy' is 
deprecated"):
-            self.assertRaises(
-                TypeError, asyncio.set_event_loop_policy, object())
-
-        with self.assertWarnsRegex(
-                DeprecationWarning, "'asyncio.get_event_loop_policy' is 
deprecated"):
-            old_policy = asyncio.get_event_loop_policy()
+        old_loop = asyncio.events._local._loop
+        loop = None
+        try:
+            self.assertRaises(TypeError, asyncio.set_event_loop, object())
 
-        policy = test_utils.DefaultEventLoopPolicy()
-        with self.assertWarnsRegex(
-                DeprecationWarning, "'asyncio.set_event_loop_policy' is 
deprecated"):
-            asyncio.set_event_loop_policy(policy)
+            loop = asyncio.new_event_loop()
+            asyncio.set_event_loop(loop)
+            self.assertIs(loop, asyncio.get_event_loop())
 
-        with self.assertWarnsRegex(
-                DeprecationWarning, "'asyncio.get_event_loop_policy' is 
deprecated"):
-            self.assertIs(policy, asyncio.get_event_loop_policy())
-            self.assertIsNot(policy, old_policy)
+            asyncio.set_event_loop(None)
+            self.assertRaises(RuntimeError, asyncio.get_event_loop)
+        finally:
+            asyncio.events._local._loop = old_loop
+            if loop is not None:
+                loop.close()
 
 
 class GetEventLoopTestsMixin:
@@ -3031,28 +2998,24 @@ async def main():
 
 
     def test_get_event_loop_returns_running_loop(self):
-        class TestError(Exception):
-            pass
-
-        class Policy(test_utils.DefaultEventLoopPolicy):
-            def get_event_loop(self):
-                raise TestError
-
-        old_policy = asyncio.events._get_event_loop_policy()
+        old_loop = asyncio.events._local._loop
+        loop = None
         try:
-            asyncio.events._set_event_loop_policy(Policy())
+            asyncio.set_event_loop(None)
             loop = asyncio.new_event_loop()
 
-            with self.assertRaises(TestError):
-                asyncio.get_event_loop()
-            asyncio.set_event_loop(None)
-            with self.assertRaises(TestError):
+            # No running loop and no loop set for the thread: the running
+            # loop is checked first, then get_event_loop() falls back to
+            # the per-thread loop and raises RuntimeError.
+            with self.assertRaisesRegex(RuntimeError, 'no current'):
                 asyncio.get_event_loop()
 
             with self.assertRaisesRegex(RuntimeError, 'no running'):
                 asyncio.get_running_loop()
             self.assertIs(asyncio._get_running_loop(), None)
 
+            # While a loop is running, get_event_loop() returns it
+            # without consulting the per-thread loop.
             async def func():
                 self.assertIs(asyncio.get_event_loop(), loop)
                 self.assertIs(asyncio.get_running_loop(), loop)
@@ -3060,15 +3023,13 @@ async def func():
 
             loop.run_until_complete(func())
 
-            asyncio.set_event_loop(loop)
-            with self.assertRaises(TestError):
-                asyncio.get_event_loop()
+            # Back outside the running loop with no loop set: raises again.
             asyncio.set_event_loop(None)
-            with self.assertRaises(TestError):
+            with self.assertRaisesRegex(RuntimeError, 'no current'):
                 asyncio.get_event_loop()
 
         finally:
-            asyncio.events._set_event_loop_policy(old_policy)
+            asyncio.events._local._loop = old_loop
             if loop is not None:
                 loop.close()
 
@@ -3078,9 +3039,10 @@ async def func():
         self.assertIs(asyncio._get_running_loop(), None)
 
     def test_get_event_loop_returns_running_loop2(self):
-        old_policy = asyncio.events._get_event_loop_policy()
+        old_loop = asyncio.events._local._loop
+        loop = None
         try:
-            
asyncio.events._set_event_loop_policy(test_utils.DefaultEventLoopPolicy())
+            asyncio.set_event_loop(None)
             loop = asyncio.new_event_loop()
             self.addCleanup(loop.close)
 
@@ -3106,7 +3068,7 @@ async def func():
                 asyncio.get_event_loop()
 
         finally:
-            asyncio.events._set_event_loop_policy(old_policy)
+            asyncio.events._local._loop = old_loop
             if loop is not None:
                 loop.close()
 
diff --git a/Lib/test/test_asyncio/test_free_threading.py 
b/Lib/test/test_asyncio/test_free_threading.py
index 0e149dadd7f1219..c60b61c7d41115f 100644
--- a/Lib/test/test_asyncio/test_free_threading.py
+++ b/Lib/test/test_asyncio/test_free_threading.py
@@ -15,7 +15,7 @@ class MyException(Exception):
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class TestFreeThreading:
diff --git a/Lib/test/test_asyncio/test_futures.py 
b/Lib/test/test_asyncio/test_futures.py
index 9385a65e52813e6..320de2180e7db7c 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -17,7 +17,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 def _fakefunc(f):
diff --git a/Lib/test/test_asyncio/test_futures2.py 
b/Lib/test/test_asyncio/test_futures2.py
index c7c0ebdac1b6760..cf90835ce87cc30 100644
--- a/Lib/test/test_asyncio/test_futures2.py
+++ b/Lib/test/test_asyncio/test_futures2.py
@@ -7,7 +7,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class FutureTests:
diff --git a/Lib/test/test_asyncio/test_graph.py 
b/Lib/test/test_asyncio/test_graph.py
index cffb2b30ad9cff6..3dde7535284b255 100644
--- a/Lib/test/test_asyncio/test_graph.py
+++ b/Lib/test/test_asyncio/test_graph.py
@@ -6,7 +6,7 @@
 
 # To prevent a warning "test altered the execution environment"
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 def capture_test_stack(*, fut=None, depth=1):
diff --git a/Lib/test/test_asyncio/test_locks.py 
b/Lib/test/test_asyncio/test_locks.py
index e025d2990a3f8a5..320a933e144813d 100644
--- a/Lib/test/test_asyncio/test_locks.py
+++ b/Lib/test/test_asyncio/test_locks.py
@@ -20,7 +20,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class LockTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_pep492.py 
b/Lib/test/test_asyncio/test_pep492.py
index 95a9f3a9a7cf716..3d4a5d8375f8a24 100644
--- a/Lib/test/test_asyncio/test_pep492.py
+++ b/Lib/test/test_asyncio/test_pep492.py
@@ -11,7 +11,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 # Test that asyncio.iscoroutine() uses collections.abc.Coroutine
diff --git a/Lib/test/test_asyncio/test_proactor_events.py 
b/Lib/test/test_asyncio/test_proactor_events.py
index edfad5e11db35e4..2f229887ad62cc8 100644
--- a/Lib/test/test_asyncio/test_proactor_events.py
+++ b/Lib/test/test_asyncio/test_proactor_events.py
@@ -18,7 +18,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 def close_transport(transport):
diff --git a/Lib/test/test_asyncio/test_protocols.py 
b/Lib/test/test_asyncio/test_protocols.py
index 29d3bd22705c6af..643199962b8af06 100644
--- a/Lib/test/test_asyncio/test_protocols.py
+++ b/Lib/test/test_asyncio/test_protocols.py
@@ -7,7 +7,7 @@
 def tearDownModule():
     # not needed for the test file but added for uniformness with all other
     # asyncio test files for the sake of unified cleanup
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class ProtocolsAbsTests(unittest.TestCase):
diff --git a/Lib/test/test_asyncio/test_queues.py 
b/Lib/test/test_asyncio/test_queues.py
index 54bbe79f81ff69b..7c1548f1948f770 100644
--- a/Lib/test/test_asyncio/test_queues.py
+++ b/Lib/test/test_asyncio/test_queues.py
@@ -6,7 +6,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class QueueBasicTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_runners.py 
b/Lib/test/test_asyncio/test_runners.py
index 8a4d7f5c796661f..b9d94833a2cccdd 100644
--- a/Lib/test/test_asyncio/test_runners.py
+++ b/Lib/test/test_asyncio/test_runners.py
@@ -12,33 +12,13 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 def interrupt_self():
     _thread.interrupt_main()
 
 
-class TestPolicy(asyncio.events._AbstractEventLoopPolicy):
-
-    def __init__(self, loop_factory):
-        self.loop_factory = loop_factory
-        self.loop = None
-
-    def get_event_loop(self):
-        # shouldn't ever be called by asyncio.run()
-        raise RuntimeError
-
-    def new_event_loop(self):
-        return self.loop_factory()
-
-    def set_event_loop(self, loop):
-        if loop is not None:
-            # we want to check if the loop is closed
-            # in BaseTest.tearDown
-            self.loop = loop
-
-
 class BaseTest(unittest.TestCase):
 
     def new_loop(self):
@@ -55,21 +35,26 @@ async def shutdown_asyncgens():
             loop.shutdown_ag_run = True
         loop.shutdown_asyncgens = shutdown_asyncgens
 
+        # Track created loops so tearDown can assert they were closed and
+        # had their async generators shut down.
+        self._loops.append(loop)
         return loop
 
     def setUp(self):
         super().setUp()
 
-        policy = TestPolicy(self.new_loop)
-        asyncio.events._set_event_loop_policy(policy)
+        # Isolate the current thread's event loop and use ``new_loop`` as
+        # an explicit ``loop_factory`` in the tests.
+        self._loops = []
+        asyncio.set_event_loop(None)
 
     def tearDown(self):
-        policy = asyncio.events._get_event_loop_policy()
-        if policy.loop is not None:
-            self.assertTrue(policy.loop.is_closed())
-            self.assertTrue(policy.loop.shutdown_ag_run)
+        if self._loops:
+            loop = self._loops[-1]
+            self.assertTrue(loop.is_closed())
+            self.assertTrue(loop.shutdown_ag_run)
 
-        asyncio.events._set_event_loop_policy(None)
+        asyncio.set_event_loop(None)
         super().tearDown()
 
 
@@ -80,7 +65,7 @@ async def main():
             await asyncio.sleep(0)
             return 42
 
-        self.assertEqual(asyncio.run(main()), 42)
+        self.assertEqual(asyncio.run(main(), loop_factory=self.new_loop), 42)
 
     def test_asyncio_run_raises(self):
         async def main():
@@ -88,7 +73,7 @@ async def main():
             raise ValueError('spam')
 
         with self.assertRaisesRegex(ValueError, 'spam'):
-            asyncio.run(main())
+            asyncio.run(main(), loop_factory=self.new_loop)
 
     def test_asyncio_run_only_coro(self):
         for o in {1, lambda: None}:
@@ -102,26 +87,26 @@ async def main(expected):
             loop = asyncio.get_event_loop()
             self.assertIs(loop.get_debug(), expected)
 
-        asyncio.run(main(False), debug=False)
-        asyncio.run(main(True), debug=True)
+        asyncio.run(main(False), debug=False, loop_factory=self.new_loop)
+        asyncio.run(main(True), debug=True, loop_factory=self.new_loop)
         with mock.patch('asyncio.coroutines._is_debug_mode', lambda: True):
-            asyncio.run(main(True))
-            asyncio.run(main(False), debug=False)
+            asyncio.run(main(True), loop_factory=self.new_loop)
+            asyncio.run(main(False), debug=False, loop_factory=self.new_loop)
         with mock.patch('asyncio.coroutines._is_debug_mode', lambda: False):
-            asyncio.run(main(True), debug=True)
-            asyncio.run(main(False))
+            asyncio.run(main(True), debug=True, loop_factory=self.new_loop)
+            asyncio.run(main(False), loop_factory=self.new_loop)
 
     def test_asyncio_run_from_running_loop(self):
         async def main():
             coro = main()
             try:
-                asyncio.run(coro)
+                asyncio.run(coro, loop_factory=self.new_loop)
             finally:
                 coro.close()  # Suppress ResourceWarning
 
         with self.assertRaisesRegex(RuntimeError,
                                     'cannot be called from a running'):
-            asyncio.run(main())
+            asyncio.run(main(), loop_factory=self.new_loop)
 
     def test_asyncio_run_cancels_hanging_tasks(self):
         lo_task = None
@@ -134,7 +119,7 @@ async def main():
             lo_task = asyncio.create_task(leftover())
             return 123
 
-        self.assertEqual(asyncio.run(main()), 123)
+        self.assertEqual(asyncio.run(main(), loop_factory=self.new_loop), 123)
         self.assertTrue(lo_task.done())
 
     def test_asyncio_run_reports_hanging_tasks_errors(self):
@@ -155,7 +140,7 @@ async def main():
             lo_task = asyncio.create_task(leftover())
             return 123
 
-        self.assertEqual(asyncio.run(main()), 123)
+        self.assertEqual(asyncio.run(main(), loop_factory=self.new_loop), 123)
         self.assertTrue(lo_task.done())
 
         call_exc_handler_mock.assert_called_with({
@@ -194,7 +179,7 @@ async def main():
             raise FancyExit
 
         with self.assertRaises(FancyExit):
-            asyncio.run(main())
+            asyncio.run(main(), loop_factory=self.new_loop)
 
         self.assertTrue(lazyboy.done())
 
@@ -208,10 +193,14 @@ async def main():
             await asyncio.sleep(0)
             return 42
 
-        policy = asyncio.events._get_event_loop_policy()
-        policy.set_event_loop = mock.Mock()
-        asyncio.run(main())
-        self.assertTrue(policy.set_event_loop.called)
+        # asyncio.run() must set the event loop for the running thread.
+        # This only happens when no explicit loop_factory is passed, so
+        # patch the default loop creation to use a mock loop and verify
+        # set_event_loop() is called.
+        with mock.patch('asyncio.events.new_event_loop', self.new_loop), \
+                mock.patch('asyncio.runners.events.set_event_loop') as 
set_event_loop:
+            asyncio.run(main())
+        self.assertTrue(set_event_loop.called)
 
     def test_asyncio_run_without_uncancel(self):
         # See https://github.com/python/cpython/issues/95097
@@ -259,9 +248,8 @@ def new_event_loop():
             loop.set_task_factory(Task)
             return loop
 
-        asyncio.events._set_event_loop_policy(TestPolicy(new_event_loop))
         with self.assertRaises(asyncio.CancelledError):
-            asyncio.run(main())
+            asyncio.run(main(), loop_factory=new_event_loop)
 
     def test_asyncio_run_loop_factory(self):
         factory = mock.Mock()
@@ -495,14 +483,17 @@ def test_set_event_loop_called_once(self):
         async def coro():
             pass
 
-        policy = asyncio.events._get_event_loop_policy()
-        policy.set_event_loop = mock.Mock()
-        runner = asyncio.Runner()
-        runner.run(coro())
-        runner.run(coro())
-
-        self.assertEqual(1, policy.set_event_loop.call_count)
-        runner.close()
+        # The runner must call set_event_loop() exactly once even when
+        # run() is called multiple times.  This only happens on the
+        # implicit loop_factory path, so patch the default loop creation.
+        with mock.patch('asyncio.events.new_event_loop', self.new_loop), \
+                mock.patch('asyncio.runners.events.set_event_loop') as 
set_event_loop:
+            runner = asyncio.Runner()
+            runner.run(coro())
+            runner.run(coro())
+
+            self.assertEqual(1, set_event_loop.call_count)
+            runner.close()
 
     def test_no_repr_is_call_on_the_task_result(self):
         # See https://github.com/python/cpython/issues/112559.
diff --git a/Lib/test/test_asyncio/test_selector_events.py 
b/Lib/test/test_asyncio/test_selector_events.py
index 4bb5d4fb816a9a6..76ba39019e36631 100644
--- a/Lib/test/test_asyncio/test_selector_events.py
+++ b/Lib/test/test_asyncio/test_selector_events.py
@@ -24,7 +24,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class TestBaseSelectorEventLoop(BaseSelectorEventLoop):
diff --git a/Lib/test/test_asyncio/test_sendfile.py 
b/Lib/test/test_asyncio/test_sendfile.py
index 7afd7de3bb936e6..2d5c06f82b3ddf1 100644
--- a/Lib/test/test_asyncio/test_sendfile.py
+++ b/Lib/test/test_asyncio/test_sendfile.py
@@ -22,7 +22,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class MySendfileProto(asyncio.Protocol):
diff --git a/Lib/test/test_asyncio/test_server.py 
b/Lib/test/test_asyncio/test_server.py
index 581ea47d2dec976..67a46085be31ce0 100644
--- a/Lib/test/test_asyncio/test_server.py
+++ b/Lib/test/test_asyncio/test_server.py
@@ -11,7 +11,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class BaseStartServer(func_tests.FunctionalTestCaseMixin):
diff --git a/Lib/test/test_asyncio/test_server_context.py 
b/Lib/test/test_asyncio/test_server_context.py
index 3f15654a1af4678..9ee5eb436e0b8dd 100644
--- a/Lib/test/test_asyncio/test_server_context.py
+++ b/Lib/test/test_asyncio/test_server_context.py
@@ -13,7 +13,7 @@
 from test.test_asyncio import utils as test_utils
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 class ServerContextvarsTestCase:
     loop_factory = None  # To be defined in subclasses
diff --git a/Lib/test/test_asyncio/test_sock_lowlevel.py 
b/Lib/test/test_asyncio/test_sock_lowlevel.py
index f32dcd589e2de22..5d825440e41ca7c 100644
--- a/Lib/test/test_asyncio/test_sock_lowlevel.py
+++ b/Lib/test/test_asyncio/test_sock_lowlevel.py
@@ -15,7 +15,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class MyProto(asyncio.Protocol):
diff --git a/Lib/test/test_asyncio/test_ssl.py 
b/Lib/test/test_asyncio/test_ssl.py
index 784c784d261dc66..902dc7e04e5809a 100644
--- a/Lib/test/test_asyncio/test_ssl.py
+++ b/Lib/test/test_asyncio/test_ssl.py
@@ -31,7 +31,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class MyBaseProto(asyncio.Protocol):
@@ -185,9 +185,6 @@ def _abort_socket_test(self, ex):
     def new_loop(self):
         return asyncio.new_event_loop()
 
-    def new_policy(self):
-        return asyncio.DefaultEventLoopPolicy()
-
     async def wait_closed(self, obj):
         if not isinstance(obj, asyncio.StreamWriter):
             return
diff --git a/Lib/test/test_asyncio/test_sslproto.py 
b/Lib/test/test_asyncio/test_sslproto.py
index 3e304c166425b0b..1b1437b51bab0ab 100644
--- a/Lib/test/test_asyncio/test_sslproto.py
+++ b/Lib/test/test_asyncio/test_sslproto.py
@@ -21,7 +21,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 @unittest.skipIf(ssl is None, 'No ssl module')
diff --git a/Lib/test/test_asyncio/test_staggered.py 
b/Lib/test/test_asyncio/test_staggered.py
index 32e4817b70d7173..693bf02001a2c3d 100644
--- a/Lib/test/test_asyncio/test_staggered.py
+++ b/Lib/test/test_asyncio/test_staggered.py
@@ -8,7 +8,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class StaggeredTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_streams.py 
b/Lib/test/test_asyncio/test_streams.py
index cae8c7c6f7c94c9..fb774895a7e52a5 100644
--- a/Lib/test/test_asyncio/test_streams.py
+++ b/Lib/test/test_asyncio/test_streams.py
@@ -18,7 +18,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class StreamTests(test_utils.TestCase):
diff --git a/Lib/test/test_asyncio/test_subprocess.py 
b/Lib/test/test_asyncio/test_subprocess.py
index 4ac6b23b7120fcc..bb1a7d6ff22be8d 100644
--- a/Lib/test/test_asyncio/test_subprocess.py
+++ b/Lib/test/test_asyncio/test_subprocess.py
@@ -38,7 +38,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class TestSubprocessTransport(base_subprocess.BaseSubprocessTransport):
diff --git a/Lib/test/test_asyncio/test_taskgroups.py 
b/Lib/test/test_asyncio/test_taskgroups.py
index 8925884b9dcf731..e1eaa60e4df85df 100644
--- a/Lib/test/test_asyncio/test_taskgroups.py
+++ b/Lib/test/test_asyncio/test_taskgroups.py
@@ -15,7 +15,7 @@
 
 # To prevent a warning "test altered the execution environment"
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class MyExc(Exception):
diff --git a/Lib/test/test_asyncio/test_tasks.py 
b/Lib/test/test_asyncio/test_tasks.py
index 56b1494c8363ca6..7a217f886b87de2 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -24,7 +24,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 async def coroutine_function():
diff --git a/Lib/test/test_asyncio/test_threads.py 
b/Lib/test/test_asyncio/test_threads.py
index 8ad5f9b2c9e7505..4300df90ff2b186 100644
--- a/Lib/test/test_asyncio/test_threads.py
+++ b/Lib/test/test_asyncio/test_threads.py
@@ -8,7 +8,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class ToThreadTests(unittest.IsolatedAsyncioTestCase):
diff --git a/Lib/test/test_asyncio/test_timeouts.py 
b/Lib/test/test_asyncio/test_timeouts.py
index f60722c48b76179..69936aae5cc81d8 100644
--- a/Lib/test/test_asyncio/test_timeouts.py
+++ b/Lib/test/test_asyncio/test_timeouts.py
@@ -9,7 +9,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 class TimeoutTests(unittest.IsolatedAsyncioTestCase):
 
diff --git a/Lib/test/test_asyncio/test_transports.py 
b/Lib/test/test_asyncio/test_transports.py
index 1781f1f67753f3a..5e743345028bec6 100644
--- a/Lib/test/test_asyncio/test_transports.py
+++ b/Lib/test/test_asyncio/test_transports.py
@@ -10,7 +10,7 @@
 def tearDownModule():
     # not needed for the test file but added for uniformness with all other
     # asyncio test files for the sake of unified cleanup
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class TransportTests(unittest.TestCase):
diff --git a/Lib/test/test_asyncio/test_unix_events.py 
b/Lib/test/test_asyncio/test_unix_events.py
index 118cb866997c51c..e88437eb2337ff0 100644
--- a/Lib/test/test_asyncio/test_unix_events.py
+++ b/Lib/test/test_asyncio/test_unix_events.py
@@ -30,7 +30,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 MOCK_ANY = mock.ANY
diff --git a/Lib/test/test_asyncio/test_waitfor.py 
b/Lib/test/test_asyncio/test_waitfor.py
index dedc6bf69d770e4..56dbafd88e45ef1 100644
--- a/Lib/test/test_asyncio/test_waitfor.py
+++ b/Lib/test/test_asyncio/test_waitfor.py
@@ -5,7 +5,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 # The following value can be used as a very small timeout:
diff --git a/Lib/test/test_asyncio/test_windows_events.py 
b/Lib/test/test_asyncio/test_windows_events.py
index 0af3368627afca7..c40391b237d57e4 100644
--- a/Lib/test/test_asyncio/test_windows_events.py
+++ b/Lib/test/test_asyncio/test_windows_events.py
@@ -19,7 +19,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class UpperProto(asyncio.Protocol):
@@ -324,40 +324,5 @@ def threadMain():
         thr.join()
 
 
-class WinPolicyTests(WindowsEventsTestCase):
-
-    def test_selector_win_policy(self):
-        async def main():
-            self.assertIsInstance(asyncio.get_running_loop(), 
asyncio.SelectorEventLoop)
-
-        old_policy = asyncio.events._get_event_loop_policy()
-        try:
-            with self.assertWarnsRegex(
-                DeprecationWarning,
-                "'asyncio.WindowsSelectorEventLoopPolicy' is deprecated",
-            ):
-                
asyncio.events._set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
-            asyncio.run(main())
-        finally:
-            asyncio.events._set_event_loop_policy(old_policy)
-
-    def test_proactor_win_policy(self):
-        async def main():
-            self.assertIsInstance(
-                asyncio.get_running_loop(),
-                asyncio.ProactorEventLoop)
-
-        old_policy = asyncio.events._get_event_loop_policy()
-        try:
-            with self.assertWarnsRegex(
-                DeprecationWarning,
-                "'asyncio.WindowsProactorEventLoopPolicy' is deprecated",
-            ):
-                
asyncio.events._set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
-            asyncio.run(main())
-        finally:
-            asyncio.events._set_event_loop_policy(old_policy)
-
-
 if __name__ == '__main__':
     unittest.main()
diff --git a/Lib/test/test_asyncio/test_windows_utils.py 
b/Lib/test/test_asyncio/test_windows_utils.py
index 509697613475953..1ac77262d1420dc 100644
--- a/Lib/test/test_asyncio/test_windows_utils.py
+++ b/Lib/test/test_asyncio/test_windows_utils.py
@@ -16,7 +16,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class PipeTests(unittest.TestCase):
diff --git a/Lib/test/test_asyncio/utils.py b/Lib/test/test_asyncio/utils.py
index 62cfcf8ceb5f2a8..e86c221d7f70259 100644
--- a/Lib/test/test_asyncio/utils.py
+++ b/Lib/test/test_asyncio/utils.py
@@ -601,9 +601,3 @@ def func():
     await asyncio.sleep(0)
     if exc is not None:
         raise exc
-
-
-if sys.platform == 'win32':
-    DefaultEventLoopPolicy = asyncio.windows_events._DefaultEventLoopPolicy
-else:
-    DefaultEventLoopPolicy = asyncio.unix_events._DefaultEventLoopPolicy
diff --git a/Lib/test/test_concurrent_futures/test_interpreter_pool.py 
b/Lib/test/test_concurrent_futures/test_interpreter_pool.py
index 7241fcc4b1e74d7..db5b89bb76ada96 100644
--- a/Lib/test/test_concurrent_futures/test_interpreter_pool.py
+++ b/Lib/test/test_concurrent_futures/test_interpreter_pool.py
@@ -518,16 +518,16 @@ class AsyncioTest(InterpretersMixin, 
testasyncio_utils.TestCase):
 
     @classmethod
     def setUpClass(cls):
-        # Most uses of asyncio will implicitly call set_event_loop_policy()
-        # with the default policy if a policy hasn't been set already.
+        # Most uses of asyncio will implicitly set a thread event loop
+        # if one hasn't been set already.
         # If that happens in a test, like here, we'll end up with a failure
         # when --fail-env-changed is used.  That's why the other tests that
-        # use asyncio are careful to set the policy back to None and why
+        # use asyncio are careful to set the loop back to None and why
         # we're careful to do so here.  We also validate that no other
-        # tests left a policy in place, just in case.
-        policy = support.maybe_get_event_loop_policy()
-        assert policy is None, policy
-        cls.addClassCleanup(lambda: 
asyncio.events._set_event_loop_policy(None))
+        # tests left a loop in place, just in case.
+        loop = support.maybe_get_event_loop()
+        assert loop is None, loop
+        cls.addClassCleanup(lambda: asyncio.set_event_loop(None))
 
     def setUp(self):
         super().setUp()
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 93e9e7a8393cb10..9d415238876c8f1 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -2321,7 +2321,7 @@ async def f():
             pass
         finally:
             loop.close()
-            asyncio.events._set_event_loop_policy(None)
+            asyncio.set_event_loop(None)
 
         self.assertEqual(buffer, [1, 2, 'MyException'])
 
diff --git a/Lib/test/test_inspect/test_inspect.py 
b/Lib/test/test_inspect/test_inspect.py
index 7351f97fd9a4b5c..495702badc3a7f1 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -2988,7 +2988,7 @@ async def asyncTearDown(self):
 
     @classmethod
     def tearDownClass(cls):
-        asyncio.events._set_event_loop_policy(None)
+        asyncio.set_event_loop(None)
 
     def _asyncgenstate(self):
         return inspect.getasyncgenstate(self.asyncgen)
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index fcd3da61a078aec..a7ea128d64055a1 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -5504,7 +5504,7 @@ def test_taskName_with_asyncio_imported(self):
                 logging.logAsyncioTasks = False
                 runner.run(make_record(self.assertIsNone))
         finally:
-            asyncio.events._set_event_loop_policy(None)
+            asyncio.set_event_loop(None)
 
     @support.requires_working_socket()
     def test_taskName_without_asyncio_imported(self):
@@ -5516,7 +5516,7 @@ def test_taskName_without_asyncio_imported(self):
                 logging.logAsyncioTasks = False
                 runner.run(make_record(self.assertIsNone))
         finally:
-            asyncio.events._set_event_loop_policy(None)
+            asyncio.set_event_loop(None)
 
 
 class BasicConfigTest(unittest.TestCase):
@@ -5841,7 +5841,7 @@ async def log_record():
                 data = f.read().strip()
             self.assertRegex(data, r'Task-\d+ - hello world')
         finally:
-            asyncio.events._set_event_loop_policy(None)
+            asyncio.set_event_loop(None)
             if handler:
                 handler.close()
 
diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py
index b88388e091312a0..c75929fa86bedd2 100644
--- a/Lib/test/test_os/test_os.py
+++ b/Lib/test/test_os/test_os.py
@@ -92,7 +92,7 @@ def requires_os_func(name):
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class MiscTests(unittest.TestCase):
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index 410f1436ed4d20a..0cf5dd2e4c10042 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -17,7 +17,7 @@
 import zipapp
 import zipfile
 
-from asyncio.events import _set_event_loop_policy
+from asyncio import set_event_loop
 from contextlib import ExitStack, redirect_stdout
 from io import StringIO
 from test import support
@@ -5312,7 +5312,7 @@ def tearDown(test):
         # Ensure that asyncio state has been cleared at the end of the test.
         # This prevents a "test altered the execution environment" warning if
         # asyncio features are used.
-        _set_event_loop_policy(None)
+        set_event_loop(None)
 
         # A doctest of pdb could have residues. For example, pdb could still
         # be running, or breakpoints might be left uncleared. These residues
diff --git a/Lib/test/test_unittest/test_async_case.py 
b/Lib/test/test_unittest/test_async_case.py
index 91d45283eb3b1bb..fae8f0e325353c5 100644
--- a/Lib/test/test_unittest/test_async_case.py
+++ b/Lib/test/test_unittest/test_async_case.py
@@ -12,7 +12,7 @@ class MyException(Exception):
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class TestCM:
@@ -480,7 +480,7 @@ def test_setup_get_event_loop(self):
 
         class TestCase1(unittest.IsolatedAsyncioTestCase):
             def setUp(self):
-                asyncio.events._get_event_loop_policy().get_event_loop()
+                asyncio.get_event_loop()
 
             async def test_demo1(self):
                 pass
@@ -490,7 +490,7 @@ async def test_demo1(self):
         self.assertTrue(result.wasSuccessful())
 
     def test_loop_factory(self):
-        asyncio.events._set_event_loop_policy(None)
+        asyncio.set_event_loop(None)
 
         class TestCase1(unittest.IsolatedAsyncioTestCase):
             loop_factory = asyncio.EventLoop
@@ -501,7 +501,7 @@ async def test_demo1(self):
         test = TestCase1('test_demo1')
         result = test.run()
         self.assertTrue(result.wasSuccessful())
-        self.assertIsNone(support.maybe_get_event_loop_policy())
+        self.assertIsNone(support.maybe_get_event_loop())
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/test/test_unittest/testmock/testasync.py 
b/Lib/test/test_unittest/testmock/testasync.py
index dc36ceeb6502e84..f96aef7325ff4c1 100644
--- a/Lib/test/test_unittest/testmock/testasync.py
+++ b/Lib/test/test_unittest/testmock/testasync.py
@@ -15,7 +15,7 @@
 
 
 def tearDownModule():
-    asyncio.events._set_event_loop_policy(None)
+    asyncio.set_event_loop(None)
 
 
 class AsyncClass:
diff --git a/Lib/unittest/async_case.py b/Lib/unittest/async_case.py
index a1c0d6c368ce8a4..36128c5fb953c52 100644
--- a/Lib/unittest/async_case.py
+++ b/Lib/unittest/async_case.py
@@ -25,13 +25,11 @@ class IsolatedAsyncioTestCase(TestCase):
     # To share contextvars between setUp(), test and tearDown() we need to 
execute
     # them inside the same task.
 
-    # Note: the test case modifies event loop policy if the policy was not 
instantiated
-    # yet, unless loop_factory=asyncio.EventLoop is set.
-    # asyncio.get_event_loop_policy() creates a default policy on demand but 
never
-    # returns None
+    # Note: the test case sets the per-thread event loop unless
+    # loop_factory=asyncio.EventLoop is set.
     # I believe this is not an issue in user level tests but python itself for 
testing
-    # should reset a policy in every test module
-    # by calling asyncio.set_event_loop_policy(None) in tearDownModule()
+    # should reset the event loop in every test module
+    # by calling asyncio.set_event_loop(None) in tearDownModule()
     # or set loop_factory=asyncio.EventLoop
 
     loop_factory = None
diff --git 
a/Misc/NEWS.d/next/Library/2026-05-19-11-42-37.gh-issue-127949.Ab3kZq.rst 
b/Misc/NEWS.d/next/Library/2026-05-19-11-42-37.gh-issue-127949.Ab3kZq.rst
new file mode 100644
index 000000000000000..c295d5fffd82fcf
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-05-19-11-42-37.gh-issue-127949.Ab3kZq.rst
@@ -0,0 +1 @@
+Remove the deprecated :mod:`asyncio` event loop policy system. Patch by Kumar 
Aditya.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 57c8d4a41a3a0df..bc0f7f3901e0eda 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -163,7 +163,7 @@ typedef struct {
     PyObject *iscoroutine_typecache;
 
     /* Imports from asyncio.events. */
-    PyObject *asyncio_get_event_loop_policy;
+    PyObject *asyncio_get_event_loop;
 
     /* Imports from asyncio.base_futures. */
     PyObject *asyncio_future_repr_func;
@@ -343,7 +343,6 @@ static PyObject *
 get_event_loop(asyncio_state *state)
 {
     PyObject *loop;
-    PyObject *policy;
 
     _PyThreadStateImpl *ts = (_PyThreadStateImpl *)_PyThreadState_GET();
     loop = Py_XNewRef(ts->asyncio_running_loop);
@@ -352,14 +351,7 @@ get_event_loop(asyncio_state *state)
         return loop;
     }
 
-    policy = PyObject_CallNoArgs(state->asyncio_get_event_loop_policy);
-    if (policy == NULL) {
-        return NULL;
-    }
-
-    loop = PyObject_CallMethodNoArgs(policy, &_Py_ID(get_event_loop));
-    Py_DECREF(policy);
-    return loop;
+    return PyObject_CallNoArgs(state->asyncio_get_event_loop);
 }
 
 
@@ -3638,12 +3630,13 @@ call_soon or similar API), this function will always 
return the
 running event loop.
 
 If there is no running event loop set, the function will return
-the result of `get_event_loop_policy().get_event_loop()` call.
+the loop set by `set_event_loop()`, or raise a RuntimeError if
+no loop has been set.
 [clinic start generated code]*/
 
 static PyObject *
 _asyncio_get_event_loop_impl(PyObject *module)
-/*[clinic end generated code: output=2a2d8b2f824c648b input=9364bf2916c8655d]*/
+/*[clinic end generated code: output=2a2d8b2f824c648b input=fa104f00dc7995dc]*/
 {
     asyncio_state *state = get_asyncio_state(module);
     return get_event_loop(state);
@@ -4192,7 +4185,7 @@ module_traverse(PyObject *mod, visitproc visit, void *arg)
     Py_VISIT(state->asyncio_mod);
     Py_VISIT(state->traceback_extract_stack);
     Py_VISIT(state->asyncio_future_repr_func);
-    Py_VISIT(state->asyncio_get_event_loop_policy);
+    Py_VISIT(state->asyncio_get_event_loop);
     Py_VISIT(state->asyncio_iscoroutine_func);
     Py_VISIT(state->asyncio_task_get_stack_func);
     Py_VISIT(state->asyncio_task_print_stack_func);
@@ -4222,7 +4215,7 @@ module_clear(PyObject *mod)
     Py_CLEAR(state->asyncio_mod);
     Py_CLEAR(state->traceback_extract_stack);
     Py_CLEAR(state->asyncio_future_repr_func);
-    Py_CLEAR(state->asyncio_get_event_loop_policy);
+    Py_CLEAR(state->asyncio_get_event_loop);
     Py_CLEAR(state->asyncio_iscoroutine_func);
     Py_CLEAR(state->asyncio_task_get_stack_func);
     Py_CLEAR(state->asyncio_task_print_stack_func);
@@ -4285,7 +4278,7 @@ module_init(asyncio_state *state)
     }
 
     WITH_MOD("asyncio.events")
-    GET_MOD_ATTR(state->asyncio_get_event_loop_policy, 
"_get_event_loop_policy")
+    GET_MOD_ATTR(state->asyncio_get_event_loop, "_get_event_loop")
 
     WITH_MOD("asyncio.base_futures")
     GET_MOD_ATTR(state->asyncio_future_repr_func, "_future_repr")
diff --git a/Modules/clinic/_asynciomodule.c.h 
b/Modules/clinic/_asynciomodule.c.h
index f07a09df5ac7ae0..8bbef6b50231250 100644
--- a/Modules/clinic/_asynciomodule.c.h
+++ b/Modules/clinic/_asynciomodule.c.h
@@ -1582,7 +1582,8 @@ PyDoc_STRVAR(_asyncio_get_event_loop__doc__,
 "running event loop.\n"
 "\n"
 "If there is no running event loop set, the function will return\n"
-"the result of `get_event_loop_policy().get_event_loop()` call.");
+"the loop set by `set_event_loop()`, or raise a RuntimeError if\n"
+"no loop has been set.");
 
 #define _ASYNCIO_GET_EVENT_LOOP_METHODDEF    \
     {"get_event_loop", (PyCFunction)_asyncio_get_event_loop, METH_NOARGS, 
_asyncio_get_event_loop__doc__},
@@ -2233,4 +2234,4 @@ _asyncio_future_discard_from_awaited_by(PyObject *module, 
PyObject *const *args,
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=32996fb47c48245b input=a9049054013a1b77]*/
+/*[clinic end generated code: output=22e74568ff49f81f input=a9049054013a1b77]*/

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to