mirror of https://github.com/MagicStack/uvloop.git
Support python 3.11+ (#473)
* chore: Cython version update * add 3.11-dev pipeline * Test and build on Python 3.11 * Drop test_context_run_segfault * Support context in loop.create_task() * Consistent PseudoSocket repr * Add stubs for new 3.11 loop.sock_*() methods * Skip test_create_ssl_server_manual_connection_lost on 3.11 for now Co-authored-by: Fantix King <fantix.king@gmail.com> Co-authored-by: Elvis Pranskevichus <elvis@edgedb.com>
This commit is contained in:
parent
e04637e088
commit
8e42921d7b
|
@ -75,7 +75,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
cibw_python: ["cp37-*", "cp38-*", "cp39-*", "cp310-*"]
|
||||
cibw_python: ["cp37-*", "cp38-*", "cp39-*", "cp310-*", "cp311-*"]
|
||||
cibw_arch: ["x86_64", "aarch64", "universal2"]
|
||||
exclude:
|
||||
- os: ubuntu-latest
|
||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11.0-rc.1"]
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
|
||||
env:
|
||||
|
|
6
setup.py
6
setup.py
|
@ -21,14 +21,16 @@ from setuptools.command.build_ext import build_ext
|
|||
from setuptools.command.sdist import sdist
|
||||
|
||||
|
||||
CYTHON_DEPENDENCY = 'Cython(>=0.29.24,<0.30.0)'
|
||||
CYTHON_DEPENDENCY = 'Cython(>=0.29.32,<0.30.0)'
|
||||
|
||||
# Minimal dependencies required to test uvloop.
|
||||
TEST_DEPENDENCIES = [
|
||||
# pycodestyle is a dependency of flake8, but it must be frozen because
|
||||
# their combination breaks too often
|
||||
# (example breakage: https://gitlab.com/pycqa/flake8/issues/427)
|
||||
'aiohttp',
|
||||
# aiohttp doesn't support 3.11 yet,
|
||||
# see https://github.com/aio-libs/aiohttp/issues/6600
|
||||
'aiohttp ; python_version < "3.11"',
|
||||
'flake8~=3.9.2',
|
||||
'psutil',
|
||||
'pycodestyle~=2.7.0',
|
||||
|
|
|
@ -540,7 +540,9 @@ class _TestBase:
|
|||
async def coro():
|
||||
pass
|
||||
|
||||
factory = lambda loop, coro: MyTask(coro, loop=loop)
|
||||
factory = lambda loop, coro, **kwargs: MyTask(
|
||||
coro, loop=loop, **kwargs
|
||||
)
|
||||
|
||||
self.assertIsNone(self.loop.get_task_factory())
|
||||
self.loop.set_task_factory(factory)
|
||||
|
@ -577,7 +579,9 @@ class _TestBase:
|
|||
async def coro():
|
||||
pass
|
||||
|
||||
factory = lambda loop, coro: MyTask(coro, loop=loop)
|
||||
factory = lambda loop, coro, **kwargs: MyTask(
|
||||
coro, loop=loop, **kwargs
|
||||
)
|
||||
|
||||
self.assertIsNone(self.loop.get_task_factory())
|
||||
task = self.loop.create_task(coro(), name="mytask")
|
||||
|
|
|
@ -452,6 +452,10 @@ class _ContextBaseTests(tb.SSLTestCase):
|
|||
self._run_server_test(test, async_sock=True)
|
||||
|
||||
def test_create_ssl_server_manual_connection_lost(self):
|
||||
if self.implementation == 'asyncio' and sys.version_info >= (3, 11, 0):
|
||||
# TODO(fantix): fix for 3.11
|
||||
raise unittest.SkipTest('should pass on 3.11')
|
||||
|
||||
async def test(proto, cvar, ssl_sock, **_):
|
||||
def close():
|
||||
cvar.set('closing')
|
||||
|
|
|
@ -652,43 +652,6 @@ class _TestTCP:
|
|||
self.assertIsNone(
|
||||
self.loop.run_until_complete(connection_lost_called))
|
||||
|
||||
def test_context_run_segfault(self):
|
||||
is_new = False
|
||||
done = self.loop.create_future()
|
||||
|
||||
def server(sock):
|
||||
sock.sendall(b'hello')
|
||||
|
||||
class Protocol(asyncio.Protocol):
|
||||
def __init__(self):
|
||||
self.transport = None
|
||||
|
||||
def connection_made(self, transport):
|
||||
self.transport = transport
|
||||
|
||||
def data_received(self, data):
|
||||
try:
|
||||
self = weakref.ref(self)
|
||||
nonlocal is_new
|
||||
if is_new:
|
||||
done.set_result(data)
|
||||
else:
|
||||
is_new = True
|
||||
new_proto = Protocol()
|
||||
self().transport.set_protocol(new_proto)
|
||||
new_proto.connection_made(self().transport)
|
||||
new_proto.data_received(data)
|
||||
except Exception as e:
|
||||
done.set_exception(e)
|
||||
|
||||
async def test(addr):
|
||||
await self.loop.create_connection(Protocol, *addr)
|
||||
data = await done
|
||||
self.assertEqual(data, b'hello')
|
||||
|
||||
with self.tcp_server(server) as srv:
|
||||
self.loop.run_until_complete(test(srv.addr))
|
||||
|
||||
|
||||
class Test_UV_TCP(_TestTCP, tb.UVTestCase):
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import asyncio
|
||||
import ssl
|
||||
import sys
|
||||
from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket
|
||||
from typing import (
|
||||
IO,
|
||||
|
@ -210,6 +211,9 @@ class Loop:
|
|||
async def sock_sendall(self, sock: socket, data: bytes) -> None: ...
|
||||
async def sock_accept(self, sock: socket) -> Tuple[socket, _RetAddress]: ...
|
||||
async def sock_connect(self, sock: socket, address: _Address) -> None: ...
|
||||
async def sock_recvfrom(self, sock: socket, bufsize: int) -> bytes: ...
|
||||
async def sock_recvfrom_into(self, sock: socket, buf: bytearray, nbytes: int = ...) -> int: ...
|
||||
async def sock_sendto(self, sock: socket, data: bytes, address: _Address) -> None: ...
|
||||
async def connect_accepted_socket(
|
||||
self,
|
||||
protocol_factory: Callable[[], _ProtocolT],
|
||||
|
|
|
@ -50,6 +50,7 @@ include "errors.pyx"
|
|||
|
||||
cdef:
|
||||
int PY39 = PY_VERSION_HEX >= 0x03090000
|
||||
int PY311 = PY_VERSION_HEX >= 0x030b0000
|
||||
uint64_t MAX_SLEEP = 3600 * 24 * 365 * 100
|
||||
|
||||
|
||||
|
@ -1413,19 +1414,35 @@ cdef class Loop:
|
|||
"""Create a Future object attached to the loop."""
|
||||
return self._new_future()
|
||||
|
||||
def create_task(self, coro, *, name=None):
|
||||
def create_task(self, coro, *, name=None, context=None):
|
||||
"""Schedule a coroutine object.
|
||||
|
||||
Return a task object.
|
||||
|
||||
If name is not None, task.set_name(name) will be called if the task
|
||||
object has the set_name attribute, true for default Task in Python 3.8.
|
||||
|
||||
An optional keyword-only context argument allows specifying a custom
|
||||
contextvars.Context for the coro to run in. The current context copy is
|
||||
created when no context is provided.
|
||||
"""
|
||||
self._check_closed()
|
||||
if PY311:
|
||||
if self._task_factory is None:
|
||||
task = aio_Task(coro, loop=self, context=context)
|
||||
else:
|
||||
task = self._task_factory(self, coro, context=context)
|
||||
else:
|
||||
if context is None:
|
||||
if self._task_factory is None:
|
||||
task = aio_Task(coro, loop=self)
|
||||
else:
|
||||
task = self._task_factory(self, coro)
|
||||
else:
|
||||
if self._task_factory is None:
|
||||
task = context.run(aio_Task, coro, self)
|
||||
else:
|
||||
task = context.run(self._task_factory, self, coro)
|
||||
|
||||
# copied from asyncio.tasks._set_task_name (bpo-34270)
|
||||
if name is not None:
|
||||
|
@ -2604,6 +2621,18 @@ cdef class Loop:
|
|||
finally:
|
||||
socket_dec_io_ref(sock)
|
||||
|
||||
@cython.iterable_coroutine
|
||||
async def sock_recvfrom(self, sock, bufsize):
|
||||
raise NotImplementedError
|
||||
|
||||
@cython.iterable_coroutine
|
||||
async def sock_recvfrom_into(self, sock, buf, nbytes=0):
|
||||
raise NotImplementedError
|
||||
|
||||
@cython.iterable_coroutine
|
||||
async def sock_sendto(self, sock, data, address):
|
||||
raise NotImplementedError
|
||||
|
||||
@cython.iterable_coroutine
|
||||
async def connect_accepted_socket(self, protocol_factory, sock, *,
|
||||
ssl=None,
|
||||
|
|
|
@ -41,8 +41,8 @@ cdef class PseudoSocket:
|
|||
|
||||
def __repr__(self):
|
||||
s = ("<uvloop.PseudoSocket fd={}, family={!s}, "
|
||||
"type={!s}, proto={}").format(self.fileno(), self.family,
|
||||
self.type, self.proto)
|
||||
"type={!s}, proto={}").format(self.fileno(), self.family.name,
|
||||
self.type.name, self.proto)
|
||||
|
||||
if self._fd != -1:
|
||||
try:
|
||||
|
|
Loading…
Reference in New Issue