Skip to content

Commit 20241b0

Browse files
committed
CPython 3.14-3.16 compat
* Add deprecation in docstring * Stop using private API, refs python/cpython#131148 * Use AbstractEventLoop instead, after @Vizonex's suggestion
1 parent 06d4232 commit 20241b0

File tree

2 files changed

+87
-44
lines changed

2 files changed

+87
-44
lines changed

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "uvloop"
33
description = "Fast implementation of asyncio event loop on top of libuv"
44
authors = [{name = "Yury Selivanov", email = "[email protected]"}]
5-
requires-python = '>=3.8.0'
5+
requires-python = '>=3.8.1'
66
readme = "README.rst"
77
license = {text = "MIT License"}
88
dynamic = ["version"]
@@ -38,9 +38,9 @@ test = [
3838
# their combination breaks too often
3939
# (example breakage: https://gitlab.com/pycqa/flake8/issues/427)
4040
'aiohttp>=3.10.5',
41-
'flake8~=5.0',
41+
'flake8~=6.1',
4242
'psutil',
43-
'pycodestyle~=2.9.0',
43+
'pycodestyle~=2.11.0',
4444
'pyOpenSSL~=23.0.0',
4545
'mypy>=0.800',
4646
]

uvloop/__init__.py

Lines changed: 84 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,12 @@
33
import sys as _sys
44
import warnings as _warnings
55

6-
try:
7-
from asyncio.events import BaseDefaultEventLoopPolicy as __BasePolicy
8-
except ImportError:
9-
# https://github.com/python/cpython/issues/131148
10-
from asyncio.events import _BaseDefaultEventLoopPolicy as __BasePolicy
11-
126
from . import includes as __includes # NOQA
137
from .loop import Loop as __BaseLoop # NOQA
148
from ._version import __version__ # NOQA
159

1610

17-
__all__ = ('new_event_loop', 'install', 'EventLoopPolicy')
11+
__all__: tuple[str, ...] = ('new_event_loop',)
1812

1913

2014
_T = _typing.TypeVar("_T")
@@ -29,18 +23,6 @@ def new_event_loop() -> Loop:
2923
return Loop()
3024

3125

32-
def install() -> None:
33-
"""A helper function to install uvloop policy."""
34-
if _sys.version_info[:2] >= (3, 12):
35-
_warnings.warn(
36-
'uvloop.install() is deprecated in favor of uvloop.run() '
37-
'starting with Python 3.12.',
38-
DeprecationWarning,
39-
stacklevel=1,
40-
)
41-
__asyncio.set_event_loop_policy(EventLoopPolicy())
42-
43-
4426
if _typing.TYPE_CHECKING:
4527
def run(
4628
main: _typing.Coroutine[_typing.Any, _typing.Any, _T],
@@ -143,30 +125,91 @@ def _cancel_all_tasks(loop: __asyncio.AbstractEventLoop) -> None:
143125
})
144126

145127

146-
class EventLoopPolicy(__BasePolicy):
147-
"""Event loop policy.
128+
if _sys.version_info[:2] < (3, 16):
129+
import threading as _threading
148130

149-
The preferred way to make your application use uvloop:
131+
__all__ += ('install', 'EventLoopPolicy')
150132

151-
>>> import asyncio
152-
>>> import uvloop
153-
>>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
154-
>>> asyncio.get_event_loop()
155-
<uvloop.Loop running=False closed=False debug=False>
156-
"""
133+
def install() -> None:
134+
"""A helper function to install uvloop policy.
157135
158-
def _loop_factory(self) -> Loop:
159-
return new_event_loop()
136+
This function is deprecated and will be removed in Python 3.16.
137+
Use `uvloop.run()` instead.
138+
"""
139+
if _sys.version_info[:2] >= (3, 12):
140+
_warnings.warn(
141+
'uvloop.install() is deprecated in favor of uvloop.run() '
142+
'starting with Python 3.12.',
143+
DeprecationWarning,
144+
stacklevel=1,
145+
)
146+
__asyncio.set_event_loop_policy(EventLoopPolicy())
147+
148+
class EventLoopPolicy(
149+
__asyncio.AbstractEventLoopPolicy # type: ignore[name-defined,misc]
150+
):
151+
"""Event loop policy for uvloop.
152+
153+
This class is deprecated and will be removed in Python 3.16.
154+
Use `uvloop.run()` instead.
155+
156+
>>> import asyncio
157+
>>> import uvloop
158+
>>> asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
159+
>>> asyncio.get_event_loop()
160+
<uvloop.Loop running=False closed=False debug=False>
161+
"""
162+
163+
def _loop_factory(self) -> Loop:
164+
return new_event_loop()
165+
166+
if _typing.TYPE_CHECKING:
167+
# EventLoopPolicy doesn't implement these, but since they are
168+
# marked as abstract in typeshed, we have to put them in so mypy
169+
# thinks the base methods are overridden. This is the same approach
170+
# taken for the Windows event loop policy classes in typeshed.
171+
def get_child_watcher(self) -> _typing.NoReturn:
172+
...
173+
174+
def set_child_watcher(
175+
self, watcher: _typing.Any
176+
) -> _typing.NoReturn:
177+
...
178+
179+
class _Local(_threading.local):
180+
_loop: _typing.Optional[Loop] = None
181+
182+
def __init__(self) -> None:
183+
self._local = self._Local()
184+
185+
def get_event_loop(self) -> Loop:
186+
"""Get the event loop for the current context.
187+
188+
Returns an instance of EventLoop or raises an exception.
189+
"""
190+
if self._local._loop is None:
191+
raise RuntimeError(
192+
'There is no current event loop in thread %r.'
193+
% _threading.current_thread().name
194+
)
195+
196+
return self._local._loop
197+
198+
def set_event_loop(self, loop: Loop) -> None:
199+
"""Set the event loop."""
200+
if loop is not None and not isinstance(
201+
loop, __asyncio.AbstractEventLoop
202+
):
203+
raise TypeError(
204+
f"loop must be an instance of AbstractEventLoop or None, "
205+
f"not '{type(loop).__name__}'"
206+
)
207+
self._local._loop = loop
160208

161-
if _typing.TYPE_CHECKING:
162-
# EventLoopPolicy doesn't implement these, but since they are marked
163-
# as abstract in typeshed, we have to put them in so mypy thinks
164-
# the base methods are overridden. This is the same approach taken
165-
# for the Windows event loop policy classes in typeshed.
166-
def get_child_watcher(self) -> _typing.NoReturn:
167-
...
209+
def new_event_loop(self) -> Loop:
210+
"""Create a new event loop.
168211
169-
def set_child_watcher(
170-
self, watcher: _typing.Any
171-
) -> _typing.NoReturn:
172-
...
212+
You must call set_event_loop() to make this the current event
213+
loop.
214+
"""
215+
return self._loop_factory()

0 commit comments

Comments
 (0)