Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port uvloop to Python 3.12 #570

Merged
merged 17 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .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-*", "cp311-*"]
cibw_python: ["cp37-*", "cp38-*", "cp39-*", "cp310-*", "cp311-*", "cp312-*"]
cibw_arch: ["x86_64", "aarch64", "universal2"]
exclude:
- os: ubuntu-latest
Expand Down Expand Up @@ -110,14 +110,11 @@ jobs:
run: |
brew install gnu-sed libtool autoconf automake

- uses: pypa/cibuildwheel@v2.9.0
- uses: pypa/cibuildwheel@v2.16.2
env:
CIBW_BUILD_VERBOSITY: 1
CIBW_BUILD: ${{ matrix.cibw_python }}
CIBW_ARCHS: ${{ matrix.cibw_arch }}
CIBW_TEST_EXTRAS: "test"
CIBW_TEST_COMMAND: "python -m unittest discover -v {project}/tests"
CIBW_TEST_COMMAND_WINDOWS: "python -m unittest discover -v {project}\\tests"
CIBW_TEST_SKIP: "*universal2:arm64"

- uses: actions/upload-artifact@v3
Expand Down
5 changes: 3 additions & 2 deletions .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", "3.11"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
os: [ubuntu-latest, macos-latest]

env:
Expand Down Expand Up @@ -58,7 +58,8 @@ jobs:
make test

- name: Test (debug build)
if: steps.release.outputs.version == 0
# XXX Re-enable 3.12 once we migrate to Cython 3
if: steps.release.outputs.version == 0 && matrix.python-version != '3.12'
run: |
make distclean && make debug && make test

Expand Down
21 changes: 21 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,27 @@ uvloop with::
Using uvloop
------------

As of uvloop 0.18, the preferred way of using it is via the
``uvloop.run()`` helper function:


.. code:: python

import uvloop

async def main():
# Main entry-point.
...

uvloop.run(main())

``uvloop.run()`` works by simply configuring ``asyncio.run()``
to use uvloop, passing all of the arguments to it, such as ``debug``,
e.g. ``uvloop.run(main(), debug=True)``.

With Python 3.11 and earlier the following alternative
snippet can be used:

.. code:: python

import asyncio
Expand Down
75 changes: 75 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
[project]
name = "uvloop"
description = "Fast implementation of asyncio event loop on top of libuv"
authors = [{name = "Yury Selivanov", email = "[email protected]"}]
requires-python = '>=3.7.0'
readme = "README.rst"
license = {text = "MIT License"}
dynamic = ["version"]
keywords = [
"asyncio",
"networking",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Framework :: AsyncIO",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"License :: OSI Approved :: MIT License",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: System :: Networking",
]

[project.urls]
github = "https://github.com/MagicStack/uvloop"

[project.optional-dependencies]
test = [
# 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>=3.8.1; python_version < "3.12"',
'aiohttp==3.9.0b0; python_version >= "3.12"',
'flake8~=5.0',
'psutil',
'pycodestyle~=2.9.0',
'pyOpenSSL~=23.0.0',
'mypy>=0.800',
'Cython(>=0.29.36,<0.30.0)',
]
docs = [
'Sphinx~=4.1.2',
'sphinxcontrib-asyncio~=0.3.0',
'sphinx_rtd_theme~=0.5.2',
]

[build-system]
requires = [
"setuptools>=60",
"wheel",
"Cython(>=0.29.36,<0.30.0)",
]
build-backend = "setuptools.build_meta"

[tool.setuptools]
zip-safe = false
packages = ["uvloop"]

[tool.cibuildwheel]
build-frontend = "build"
test-extras = "test"
test-command = "python -m unittest discover -v {project}/tests"

[tool.pytest.ini_options]
addopts = "--capture=no --assert=plain --strict-markers --tb=native --import-mode=importlib"
testpaths = "tests"
filterwarnings = "default"
4 changes: 0 additions & 4 deletions pytest.ini

This file was deleted.

63 changes: 1 addition & 62 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,7 @@
from setuptools.command.sdist import sdist


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>=3.8.1',
'flake8~=5.0',
'psutil',
'pycodestyle~=2.9.0',
'pyOpenSSL~=23.0.0',
'mypy>=0.800',
CYTHON_DEPENDENCY,
]

# Dependencies required to build documentation.
DOC_DEPENDENCIES = [
'Sphinx~=4.1.2',
'sphinxcontrib-asyncio~=0.3.0',
'sphinx_rtd_theme~=0.5.2',
]

EXTRA_DEPENDENCIES = {
'docs': DOC_DEPENDENCIES,
'test': TEST_DEPENDENCIES,
# Dependencies required to develop uvloop.
'dev': [
CYTHON_DEPENDENCY,
'pytest>=3.6.0',
] + DOC_DEPENDENCIES + TEST_DEPENDENCIES
}


CYTHON_DEPENDENCY = 'Cython(>=0.29.36,<0.30.0)'
MACHINE = platform.machine()
MODULES_CFLAGS = [os.getenv('UVLOOP_OPT_CFLAGS', '-O2')]
_ROOT = pathlib.Path(__file__).parent
Expand Down Expand Up @@ -245,10 +212,6 @@ def build_extensions(self):
super().build_extensions()


with open(str(_ROOT / 'README.rst')) as f:
readme = f.read()


with open(str(_ROOT / 'uvloop' / '_version.py')) as f:
for line in f:
if line.startswith('__version__ ='):
Expand All @@ -268,16 +231,7 @@ def build_extensions(self):


setup(
name='uvloop',
description='Fast implementation of asyncio event loop on top of libuv',
long_description=readme,
url='http://github.com/MagicStack/uvloop',
license='MIT',
author='Yury Selivanov',
author_email='[email protected]',
platforms=['macOS', 'POSIX'],
version=VERSION,
packages=['uvloop'],
cmdclass={
'sdist': uvloop_sdist,
'build_ext': uvloop_build_ext
Expand All @@ -291,20 +245,5 @@ def build_extensions(self):
extra_compile_args=MODULES_CFLAGS
),
],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Framework :: AsyncIO',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'License :: OSI Approved :: Apache Software License',
'License :: OSI Approved :: MIT License',
'Intended Audience :: Developers',
],
include_package_data=True,
extras_require=EXTRA_DEPENDENCIES,
setup_requires=setup_requires,
python_requires='>=3.7',
)
5 changes: 0 additions & 5 deletions tests/cython_helper.pyx

This file was deleted.

19 changes: 17 additions & 2 deletions tests/test_aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand Down Expand Up @@ -48,6 +49,14 @@ async def test():
self.loop.run_until_complete(runner.cleanup())

def test_aiohttp_graceful_shutdown(self):
if self.implementation == 'asyncio' and sys.version_info >= (3, 12, 0):
# In Python 3.12.0, asyncio.Server.wait_closed() waits for all
# existing connections to complete, before aiohttp sends
# on_shutdown signals.
# https://github.com/aio-libs/aiohttp/issues/7675#issuecomment-1752143748
# https://github.com/python/cpython/pull/98582
raise unittest.SkipTest('bug in aiohttp: #7675')

async def websocket_handler(request):
ws = aiohttp.web.WebSocketResponse()
await ws.prepare(request)
Expand All @@ -73,7 +82,13 @@ async def on_shutdown(app):

runner = aiohttp.web.AppRunner(app)
self.loop.run_until_complete(runner.setup())
site = aiohttp.web.TCPSite(runner, '0.0.0.0', '0')
site = aiohttp.web.TCPSite(
runner,
'0.0.0.0',
0,
# https://github.com/aio-libs/aiohttp/pull/7188
shutdown_timeout=0.1,
)
self.loop.run_until_complete(site.start())
port = site._server.sockets[0].getsockname()[1]

Expand All @@ -90,7 +105,7 @@ async def client():
async def stop():
await asyncio.sleep(0.1)
try:
await asyncio.wait_for(runner.cleanup(), timeout=0.1)
await asyncio.wait_for(runner.cleanup(), timeout=0.5)
finally:
try:
client_task.cancel()
Expand Down
4 changes: 1 addition & 3 deletions tests/test_dealloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,12 @@ def test_dealloc_1(self):
async def test():
prog = '''\
import uvloop
import asyncio

async def foo():
return 42

def main():
uvloop.install()
loop = asyncio.get_event_loop()
loop = uvloop.new_event_loop()
loop.set_debug(True)
loop.run_until_complete(foo())
# Do not close the loop on purpose: let __dealloc__ methods run.
Expand Down
23 changes: 12 additions & 11 deletions tests/test_libuv_api.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
from uvloop import _testbase as tb
from uvloop.loop import libuv_get_loop_t_ptr, libuv_get_version
from uvloop.loop import _testhelper_unwrap_capsuled_pointer as unwrap


class Test_UV_libuv(tb.UVTestCase):
def test_libuv_get_loop_t_ptr(self):
loop = self.new_loop()
cap1 = libuv_get_loop_t_ptr(loop)
cap2 = libuv_get_loop_t_ptr(loop)
cap3 = libuv_get_loop_t_ptr(self.new_loop())
loop1 = self.new_loop()
cap1 = libuv_get_loop_t_ptr(loop1)
cap2 = libuv_get_loop_t_ptr(loop1)

import pyximport
loop2 = self.new_loop()
cap3 = libuv_get_loop_t_ptr(loop2)

pyximport.install()

import cython_helper

self.assertTrue(cython_helper.capsule_equals(cap1, cap2))
self.assertFalse(cython_helper.capsule_equals(cap1, cap3))
try:
self.assertEqual(unwrap(cap1), unwrap(cap2))
self.assertNotEqual(unwrap(cap1), unwrap(cap3))
finally:
loop1.close()
loop2.close()

def test_libuv_get_version(self):
self.assertGreater(libuv_get_version(), 0)
3 changes: 1 addition & 2 deletions tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -851,8 +851,7 @@ async def test():
)
await proc.communicate()

uvloop.install()
asyncio.run(test())
uvloop.run(test())

stdin, stdout, stderr = dups
(r, w), = pipes
Expand Down
39 changes: 39 additions & 0 deletions tests/test_runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import asyncio
import unittest
import uvloop


class TestSourceCode(unittest.TestCase):

def test_uvloop_run_1(self):
CNT = 0

async def main():
nonlocal CNT
CNT += 1

loop = asyncio.get_running_loop()

self.assertTrue(isinstance(loop, uvloop.Loop))
self.assertTrue(loop.get_debug())

return 'done'

result = uvloop.run(main(), debug=True)

self.assertEqual(result, 'done')
self.assertEqual(CNT, 1)

def test_uvloop_run_2(self):

async def main():
pass

coro = main()
with self.assertRaisesRegex(TypeError, ' a non-uvloop event loop'):
uvloop.run(
coro,
loop_factory=asyncio.DefaultEventLoopPolicy().new_event_loop,
)

coro.close()
Loading
Loading