From 3dd22b3462b85d1ad869448be425cd5e398db165 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 20 Oct 2024 21:34:46 +0100 Subject: [PATCH 01/11] test on 3.14 --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ed6accb9..c6537efb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,6 +22,7 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" os: [ubuntu-latest, macos-latest] env: From 4eea0052b85110cb5e97d475189369a3476d7891 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 20 Oct 2024 21:42:23 +0100 Subject: [PATCH 02/11] deal with removed AbstractChildWatcher --- uvloop/includes/stdlib.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvloop/includes/stdlib.pxi b/uvloop/includes/stdlib.pxi index 4152b8a7..2e91f5ae 100644 --- a/uvloop/includes/stdlib.pxi +++ b/uvloop/includes/stdlib.pxi @@ -44,7 +44,7 @@ cdef aio_isfuture = getattr(asyncio, 'isfuture', None) cdef aio_get_running_loop = getattr(asyncio, '_get_running_loop', None) cdef aio_set_running_loop = getattr(asyncio, '_set_running_loop', None) cdef aio_debug_wrapper = getattr(asyncio.coroutines, 'debug_wrapper', None) -cdef aio_AbstractChildWatcher = asyncio.AbstractChildWatcher +cdef aio_AbstractChildWatcher = getattr(asyncio, "AbstractChildWatcher", object()) cdef aio_Transport = asyncio.Transport cdef aio_FlowControlMixin = asyncio.transports._FlowControlMixin From 79f008b4b0c1add675e98100dd1b5400382c1014 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Sun, 14 Sep 2025 10:15:41 -0400 Subject: [PATCH 03/11] Fix marginal case typing In extreme cases, isinstance(..., ()) may still return False instead of raising a TypeError --- uvloop/includes/stdlib.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvloop/includes/stdlib.pxi b/uvloop/includes/stdlib.pxi index 2e91f5ae..02605a08 100644 --- a/uvloop/includes/stdlib.pxi +++ b/uvloop/includes/stdlib.pxi @@ -44,7 +44,7 @@ cdef aio_isfuture = getattr(asyncio, 'isfuture', None) cdef aio_get_running_loop = getattr(asyncio, '_get_running_loop', None) cdef aio_set_running_loop = getattr(asyncio, '_set_running_loop', None) cdef aio_debug_wrapper = getattr(asyncio.coroutines, 'debug_wrapper', None) -cdef aio_AbstractChildWatcher = getattr(asyncio, "AbstractChildWatcher", object()) +cdef aio_AbstractChildWatcher = getattr(asyncio, "AbstractChildWatcher", ()) cdef aio_Transport = asyncio.Transport cdef aio_FlowControlMixin = asyncio.transports._FlowControlMixin From 74e109e6114b21ebf036af59e95baa1f7dfa6e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 12 Mar 2025 14:36:01 +0100 Subject: [PATCH 04/11] Workaround missing BaseDefaultEventLoopPolicy in Python 3.14 This uses private API now. Proper fix pending discussion in https://github.com/python/cpython/issues/131148 --- uvloop/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/uvloop/__init__.py b/uvloop/__init__.py index 9bb6592b..283a5b03 100644 --- a/uvloop/__init__.py +++ b/uvloop/__init__.py @@ -3,7 +3,11 @@ import sys as _sys import warnings as _warnings -from asyncio.events import BaseDefaultEventLoopPolicy as __BasePolicy +try: + from asyncio.events import BaseDefaultEventLoopPolicy as __BasePolicy +except ImportError: + # https://github.com/python/cpython/issues/131148 + from asyncio.events import _BaseDefaultEventLoopPolicy as __BasePolicy from . import includes as __includes # NOQA from .loop import Loop as __BaseLoop # NOQA From 06d4232a639a4983bedb8cb1baf542b03f198109 Mon Sep 17 00:00:00 2001 From: Paulo Cheque Date: Sun, 14 Sep 2025 10:07:26 -0300 Subject: [PATCH 05/11] include py 3.14 in CI scripts --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29e7a7a4..d49bb4e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,6 +83,7 @@ jobs: - "cp311-*" - "cp312-*" - "cp313-*" + - "cp314-*" cibw_arch: ["x86_64", "aarch64", "universal2"] exclude: - os: ubuntu-latest From 20241b0cdf1ae47f4ba3ff919a3fee3e960228cb Mon Sep 17 00:00:00 2001 From: Fantix King Date: Sun, 14 Sep 2025 11:20:25 -0400 Subject: [PATCH 06/11] CPython 3.14-3.16 compat * Add deprecation in docstring * Stop using private API, refs https://github.com/python/cpython/issues/131148 * Use AbstractEventLoop instead, after @Vizonex's suggestion --- pyproject.toml | 6 +-- uvloop/__init__.py | 125 ++++++++++++++++++++++++++++++--------------- 2 files changed, 87 insertions(+), 44 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a656b4f8..d7ee1f1f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "uvloop" description = "Fast implementation of asyncio event loop on top of libuv" authors = [{name = "Yury Selivanov", email = "yury@magic.io"}] -requires-python = '>=3.8.0' +requires-python = '>=3.8.1' readme = "README.rst" license = {text = "MIT License"} dynamic = ["version"] @@ -38,9 +38,9 @@ test = [ # their combination breaks too often # (example breakage: https://gitlab.com/pycqa/flake8/issues/427) 'aiohttp>=3.10.5', - 'flake8~=5.0', + 'flake8~=6.1', 'psutil', - 'pycodestyle~=2.9.0', + 'pycodestyle~=2.11.0', 'pyOpenSSL~=23.0.0', 'mypy>=0.800', ] diff --git a/uvloop/__init__.py b/uvloop/__init__.py index 283a5b03..27b21b4b 100644 --- a/uvloop/__init__.py +++ b/uvloop/__init__.py @@ -3,18 +3,12 @@ import sys as _sys import warnings as _warnings -try: - from asyncio.events import BaseDefaultEventLoopPolicy as __BasePolicy -except ImportError: - # https://github.com/python/cpython/issues/131148 - from asyncio.events import _BaseDefaultEventLoopPolicy as __BasePolicy - from . import includes as __includes # NOQA from .loop import Loop as __BaseLoop # NOQA from ._version import __version__ # NOQA -__all__ = ('new_event_loop', 'install', 'EventLoopPolicy') +__all__: tuple[str, ...] = ('new_event_loop',) _T = _typing.TypeVar("_T") @@ -29,18 +23,6 @@ def new_event_loop() -> Loop: return Loop() -def install() -> None: - """A helper function to install uvloop policy.""" - if _sys.version_info[:2] >= (3, 12): - _warnings.warn( - 'uvloop.install() is deprecated in favor of uvloop.run() ' - 'starting with Python 3.12.', - DeprecationWarning, - stacklevel=1, - ) - __asyncio.set_event_loop_policy(EventLoopPolicy()) - - if _typing.TYPE_CHECKING: def run( main: _typing.Coroutine[_typing.Any, _typing.Any, _T], @@ -143,30 +125,91 @@ def _cancel_all_tasks(loop: __asyncio.AbstractEventLoop) -> None: }) -class EventLoopPolicy(__BasePolicy): - """Event loop policy. +if _sys.version_info[:2] < (3, 16): + import threading as _threading - The preferred way to make your application use uvloop: + __all__ += ('install', 'EventLoopPolicy') - >>> import asyncio - >>> import uvloop - >>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) - >>> asyncio.get_event_loop() - - """ + def install() -> None: + """A helper function to install uvloop policy. - def _loop_factory(self) -> Loop: - return new_event_loop() + This function is deprecated and will be removed in Python 3.16. + Use `uvloop.run()` instead. + """ + if _sys.version_info[:2] >= (3, 12): + _warnings.warn( + 'uvloop.install() is deprecated in favor of uvloop.run() ' + 'starting with Python 3.12.', + DeprecationWarning, + stacklevel=1, + ) + __asyncio.set_event_loop_policy(EventLoopPolicy()) + + class EventLoopPolicy( + __asyncio.AbstractEventLoopPolicy # type: ignore[name-defined,misc] + ): + """Event loop policy for uvloop. + + This class is deprecated and will be removed in Python 3.16. + Use `uvloop.run()` instead. + + >>> import asyncio + >>> import uvloop + >>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + >>> asyncio.get_event_loop() + + """ + + def _loop_factory(self) -> Loop: + return new_event_loop() + + if _typing.TYPE_CHECKING: + # EventLoopPolicy doesn't implement these, but since they are + # marked as abstract in typeshed, we have to put them in so mypy + # thinks the base methods are overridden. This is the same approach + # taken for the Windows event loop policy classes in typeshed. + def get_child_watcher(self) -> _typing.NoReturn: + ... + + def set_child_watcher( + self, watcher: _typing.Any + ) -> _typing.NoReturn: + ... + + class _Local(_threading.local): + _loop: _typing.Optional[Loop] = None + + def __init__(self) -> None: + self._local = self._Local() + + def get_event_loop(self) -> Loop: + """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: Loop) -> None: + """Set the event loop.""" + if loop is not None and not isinstance( + loop, __asyncio.AbstractEventLoop + ): + raise TypeError( + f"loop must be an instance of AbstractEventLoop or None, " + f"not '{type(loop).__name__}'" + ) + self._local._loop = loop - if _typing.TYPE_CHECKING: - # EventLoopPolicy doesn't implement these, but since they are marked - # as abstract in typeshed, we have to put them in so mypy thinks - # the base methods are overridden. This is the same approach taken - # for the Windows event loop policy classes in typeshed. - def get_child_watcher(self) -> _typing.NoReturn: - ... + def new_event_loop(self) -> Loop: + """Create a new event loop. - def set_child_watcher( - self, watcher: _typing.Any - ) -> _typing.NoReturn: - ... + You must call set_event_loop() to make this the current event + loop. + """ + return self._loop_factory() From 540e6abfdb806a65ed7c09d790790d85267caa1d Mon Sep 17 00:00:00 2001 From: Fantix King Date: Sun, 14 Sep 2025 12:08:42 -0400 Subject: [PATCH 07/11] Fix dunder in class --- uvloop/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/uvloop/__init__.py b/uvloop/__init__.py index 27b21b4b..bfa738e9 100644 --- a/uvloop/__init__.py +++ b/uvloop/__init__.py @@ -9,12 +9,13 @@ __all__: tuple[str, ...] = ('new_event_loop',) +_AbstractEventLoop = __asyncio.AbstractEventLoop _T = _typing.TypeVar("_T") -class Loop(__BaseLoop, __asyncio.AbstractEventLoop): # type: ignore[misc] +class Loop(__BaseLoop, _AbstractEventLoop): # type: ignore[misc] pass @@ -100,7 +101,7 @@ async def wrapper(): ) -def _cancel_all_tasks(loop: __asyncio.AbstractEventLoop) -> None: +def _cancel_all_tasks(loop: _AbstractEventLoop) -> None: # Copied from python/cpython to_cancel = __asyncio.all_tasks(loop) @@ -197,9 +198,7 @@ def get_event_loop(self) -> Loop: def set_event_loop(self, loop: Loop) -> None: """Set the event loop.""" - if loop is not None and not isinstance( - loop, __asyncio.AbstractEventLoop - ): + if loop is not None and not isinstance(loop, _AbstractEventLoop): raise TypeError( f"loop must be an instance of AbstractEventLoop or None, " f"not '{type(loop).__name__}'" From ae50a8e1d88bd8724d3829daf527ba9c96ec5b35 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Sun, 14 Sep 2025 18:11:28 -0400 Subject: [PATCH 08/11] Fix tests --- uvloop/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/uvloop/__init__.py b/uvloop/__init__.py index bfa738e9..80c2bfa7 100644 --- a/uvloop/__init__.py +++ b/uvloop/__init__.py @@ -8,7 +8,7 @@ from ._version import __version__ # NOQA -__all__: tuple[str, ...] = ('new_event_loop',) +__all__: _typing.Tuple[str, ...] = ('new_event_loop',) _AbstractEventLoop = __asyncio.AbstractEventLoop @@ -147,7 +147,7 @@ def install() -> None: __asyncio.set_event_loop_policy(EventLoopPolicy()) class EventLoopPolicy( - __asyncio.AbstractEventLoopPolicy # type: ignore[name-defined,misc] + __asyncio.AbstractEventLoopPolicy # type: ignore ): """Event loop policy for uvloop. @@ -178,12 +178,12 @@ def set_child_watcher( ... class _Local(_threading.local): - _loop: _typing.Optional[Loop] = None + _loop: _typing.Optional[_AbstractEventLoop] = None def __init__(self) -> None: self._local = self._Local() - def get_event_loop(self) -> Loop: + def get_event_loop(self) -> _AbstractEventLoop: """Get the event loop for the current context. Returns an instance of EventLoop or raises an exception. @@ -196,7 +196,7 @@ def get_event_loop(self) -> Loop: return self._local._loop - def set_event_loop(self, loop: Loop) -> None: + def set_event_loop(self, loop: _AbstractEventLoop) -> None: """Set the event loop.""" if loop is not None and not isinstance(loop, _AbstractEventLoop): raise TypeError( From d66df0385abff59ddae077f37cc826918b8222b4 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Sun, 14 Sep 2025 18:39:32 -0400 Subject: [PATCH 09/11] Define EventLoopPolicy lazily to avoid warning --- uvloop/__init__.py | 168 ++++++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 77 deletions(-) diff --git a/uvloop/__init__.py b/uvloop/__init__.py index 80c2bfa7..986e1574 100644 --- a/uvloop/__init__.py +++ b/uvloop/__init__.py @@ -127,88 +127,102 @@ def _cancel_all_tasks(loop: _AbstractEventLoop) -> None: if _sys.version_info[:2] < (3, 16): - import threading as _threading - __all__ += ('install', 'EventLoopPolicy') - def install() -> None: - """A helper function to install uvloop policy. - - This function is deprecated and will be removed in Python 3.16. - Use `uvloop.run()` instead. - """ - if _sys.version_info[:2] >= (3, 12): - _warnings.warn( - 'uvloop.install() is deprecated in favor of uvloop.run() ' - 'starting with Python 3.12.', - DeprecationWarning, - stacklevel=1, - ) - __asyncio.set_event_loop_policy(EventLoopPolicy()) - - class EventLoopPolicy( - __asyncio.AbstractEventLoopPolicy # type: ignore - ): - """Event loop policy for uvloop. - - This class is deprecated and will be removed in Python 3.16. - Use `uvloop.run()` instead. - - >>> import asyncio - >>> import uvloop - >>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) - >>> asyncio.get_event_loop() - - """ - - def _loop_factory(self) -> Loop: - return new_event_loop() - - if _typing.TYPE_CHECKING: - # EventLoopPolicy doesn't implement these, but since they are - # marked as abstract in typeshed, we have to put them in so mypy - # thinks the base methods are overridden. This is the same approach - # taken for the Windows event loop policy classes in typeshed. - def get_child_watcher(self) -> _typing.NoReturn: - ... - - def set_child_watcher( - self, watcher: _typing.Any - ) -> _typing.NoReturn: - ... + def __getattr__(name: str) -> _typing.Any: + if name not in __all__: + raise AttributeError(f"module 'uvloop' has no attribute '{name}'") - class _Local(_threading.local): - _loop: _typing.Optional[_AbstractEventLoop] = None + import threading - def __init__(self) -> None: - self._local = self._Local() + def install() -> None: + """A helper function to install uvloop policy. - def get_event_loop(self) -> _AbstractEventLoop: - """Get the event loop for the current context. - - Returns an instance of EventLoop or raises an exception. + This function is deprecated and will be removed in Python 3.16. + Use `uvloop.run()` instead. """ - 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: _AbstractEventLoop) -> None: - """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, " - f"not '{type(loop).__name__}'" + if _sys.version_info[:2] >= (3, 12): + _warnings.warn( + 'uvloop.install() is deprecated in favor of uvloop.run() ' + 'starting with Python 3.12.', + DeprecationWarning, + stacklevel=1, ) - self._local._loop = loop - - def new_event_loop(self) -> Loop: - """Create a new event loop. - - You must call set_event_loop() to make this the current event - loop. + __asyncio.set_event_loop_policy(EventLoopPolicy()) + + class EventLoopPolicy( + # This is to avoid a mypy error about AbstractEventLoopPolicy + getattr(__asyncio, 'AbstractEventLoopPolicy') # type: ignore[misc] + ): + """Event loop policy for uvloop. + + This class is deprecated and will be removed in Python 3.16. + Use `uvloop.run()` instead. + + >>> import asyncio + >>> import uvloop + >>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + >>> asyncio.get_event_loop() + """ - return self._loop_factory() + + def _loop_factory(self) -> Loop: + return new_event_loop() + + if _typing.TYPE_CHECKING: + # EventLoopPolicy doesn't implement these, but since they are + # marked as abstract in typeshed, we have to put them in so + # mypy thinks the base methods are overridden. This is the same + # approach taken for the Windows event loop policy classes in + # typeshed. + def get_child_watcher(self) -> _typing.NoReturn: + ... + + def set_child_watcher( + self, watcher: _typing.Any + ) -> _typing.NoReturn: + ... + + class _Local(threading.local): + _loop: _typing.Optional[_AbstractEventLoop] = None + + def __init__(self) -> None: + self._local = self._Local() + + def get_event_loop(self) -> _AbstractEventLoop: + """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: _typing.Optional[_AbstractEventLoop] + ) -> None: + """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 " + f"None, not '{type(loop).__name__}'" + ) + self._local._loop = loop + + def new_event_loop(self) -> Loop: + """Create a new event loop. + + You must call set_event_loop() to make this the current event + loop. + """ + return self._loop_factory() + + globals()['install'] = install + globals()['EventLoopPolicy'] = EventLoopPolicy + return globals()[name] From 64e89eaa09830a569d22952067cc84697240beea Mon Sep 17 00:00:00 2001 From: Fantix King Date: Sun, 14 Sep 2025 19:56:08 -0400 Subject: [PATCH 10/11] Add `uvloop.run` to `uvloop.__all__` --- uvloop/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvloop/__init__.py b/uvloop/__init__.py index 986e1574..955e25e4 100644 --- a/uvloop/__init__.py +++ b/uvloop/__init__.py @@ -8,7 +8,7 @@ from ._version import __version__ # NOQA -__all__: _typing.Tuple[str, ...] = ('new_event_loop',) +__all__: _typing.Tuple[str, ...] = ('new_event_loop', 'run') _AbstractEventLoop = __asyncio.AbstractEventLoop From 93d25d2af2933a4d96cffc347ce6d6b1321d3ec7 Mon Sep 17 00:00:00 2001 From: Fantix King Date: Mon, 15 Sep 2025 09:49:57 -0400 Subject: [PATCH 11/11] Add error message for 3.16 --- uvloop/__init__.py | 191 +++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 93 deletions(-) diff --git a/uvloop/__init__.py b/uvloop/__init__.py index 955e25e4..638b874c 100644 --- a/uvloop/__init__.py +++ b/uvloop/__init__.py @@ -126,103 +126,108 @@ def _cancel_all_tasks(loop: _AbstractEventLoop) -> None: }) -if _sys.version_info[:2] < (3, 16): - __all__ += ('install', 'EventLoopPolicy') +_deprecated_names = ('install', 'EventLoopPolicy') - def __getattr__(name: str) -> _typing.Any: - if name not in __all__: - raise AttributeError(f"module 'uvloop' has no attribute '{name}'") - import threading +if _sys.version_info[:2] < (3, 16): + __all__ += _deprecated_names + + +def __getattr__(name: str) -> _typing.Any: + if name not in _deprecated_names: + raise AttributeError(f"module 'uvloop' has no attribute '{name}'") + elif _sys.version_info[:2] >= (3, 16): + raise AttributeError( + f"module 'uvloop' has no attribute '{name}' " + f"(it was removed in Python 3.16, use uvloop.run() instead)" + ) + + import threading + + def install() -> None: + """A helper function to install uvloop policy. + + This function is deprecated and will be removed in Python 3.16. + Use `uvloop.run()` instead. + """ + if _sys.version_info[:2] >= (3, 12): + _warnings.warn( + 'uvloop.install() is deprecated in favor of uvloop.run() ' + 'starting with Python 3.12.', + DeprecationWarning, + stacklevel=1, + ) + __asyncio.set_event_loop_policy(EventLoopPolicy()) + + class EventLoopPolicy( + # This is to avoid a mypy error about AbstractEventLoopPolicy + getattr(__asyncio, 'AbstractEventLoopPolicy') # type: ignore[misc] + ): + """Event loop policy for uvloop. + + This class is deprecated and will be removed in Python 3.16. + Use `uvloop.run()` instead. + + >>> import asyncio + >>> import uvloop + >>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + >>> asyncio.get_event_loop() + + """ + + def _loop_factory(self) -> Loop: + return new_event_loop() + + if _typing.TYPE_CHECKING: + # EventLoopPolicy doesn't implement these, but since they are + # marked as abstract in typeshed, we have to put them in so mypy + # thinks the base methods are overridden. This is the same approach + # taken for the Windows event loop policy classes in typeshed. + def get_child_watcher(self) -> _typing.NoReturn: + ... + + def set_child_watcher( + self, watcher: _typing.Any + ) -> _typing.NoReturn: + ... + + class _Local(threading.local): + _loop: _typing.Optional[_AbstractEventLoop] = None + + def __init__(self) -> None: + self._local = self._Local() + + def get_event_loop(self) -> _AbstractEventLoop: + """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 + ) - def install() -> None: - """A helper function to install uvloop policy. + return self._local._loop - This function is deprecated and will be removed in Python 3.16. - Use `uvloop.run()` instead. - """ - if _sys.version_info[:2] >= (3, 12): - _warnings.warn( - 'uvloop.install() is deprecated in favor of uvloop.run() ' - 'starting with Python 3.12.', - DeprecationWarning, - stacklevel=1, + def set_event_loop( + self, loop: _typing.Optional[_AbstractEventLoop] + ) -> None: + """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, " + f"not '{type(loop).__name__}'" ) - __asyncio.set_event_loop_policy(EventLoopPolicy()) - - class EventLoopPolicy( - # This is to avoid a mypy error about AbstractEventLoopPolicy - getattr(__asyncio, 'AbstractEventLoopPolicy') # type: ignore[misc] - ): - """Event loop policy for uvloop. - - This class is deprecated and will be removed in Python 3.16. - Use `uvloop.run()` instead. - - >>> import asyncio - >>> import uvloop - >>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) - >>> asyncio.get_event_loop() - + self._local._loop = loop + + def new_event_loop(self) -> Loop: + """Create a new event loop. + + You must call set_event_loop() to make this the current event loop. """ + return self._loop_factory() - def _loop_factory(self) -> Loop: - return new_event_loop() - - if _typing.TYPE_CHECKING: - # EventLoopPolicy doesn't implement these, but since they are - # marked as abstract in typeshed, we have to put them in so - # mypy thinks the base methods are overridden. This is the same - # approach taken for the Windows event loop policy classes in - # typeshed. - def get_child_watcher(self) -> _typing.NoReturn: - ... - - def set_child_watcher( - self, watcher: _typing.Any - ) -> _typing.NoReturn: - ... - - class _Local(threading.local): - _loop: _typing.Optional[_AbstractEventLoop] = None - - def __init__(self) -> None: - self._local = self._Local() - - def get_event_loop(self) -> _AbstractEventLoop: - """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: _typing.Optional[_AbstractEventLoop] - ) -> None: - """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 " - f"None, not '{type(loop).__name__}'" - ) - self._local._loop = loop - - def new_event_loop(self) -> Loop: - """Create a new event loop. - - You must call set_event_loop() to make this the current event - loop. - """ - return self._loop_factory() - - globals()['install'] = install - globals()['EventLoopPolicy'] = EventLoopPolicy - return globals()[name] + globals()['install'] = install + globals()['EventLoopPolicy'] = EventLoopPolicy + return globals()[name]