Skip to content

Commit

Permalink
Support python 3.11+ (#473)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people committed Sep 1, 2022
1 parent e04637e commit 8e42921
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@
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',
Expand Down
8 changes: 6 additions & 2 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,9 @@ class MyTask(asyncio.Task):
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)
Expand Down Expand Up @@ -577,7 +579,9 @@ def get_name(self):
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")
Expand Down
4 changes: 4 additions & 0 deletions tests/test_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,10 @@ def close():
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')
Expand Down
37 changes: 0 additions & 37 deletions tests/test_tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,43 +652,6 @@ async def runner():
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):

Expand Down
4 changes: 4 additions & 0 deletions uvloop/loop.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import ssl
import sys
from socket import AddressFamily, SocketKind, _Address, _RetAddress, socket
from typing import (
IO,
Expand Down Expand Up @@ -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],
Expand Down
37 changes: 33 additions & 4 deletions uvloop/loop.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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 self._task_factory is None:
task = aio_Task(coro, loop=self)
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:
task = self._task_factory(self, coro)
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:
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions uvloop/pseudosock.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 8e42921

Please sign in to comment.