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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 = "yury@magic.io"}]
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 numberDiff line numberDiff 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 DownExpand 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='yury@magic.io',
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 numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 = "yury@magic.io"}]
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 numberDiff line numberDiff 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 DownExpand 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='yury@magic.io',
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 numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 = "yury@magic.io"}]
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 numberDiff line numberDiff 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 DownExpand 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='yury@magic.io',
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 numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 = "yury@magic.io"}]
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 numberDiff line numberDiff 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 DownExpand 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='yury@magic.io',
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 numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 = "yury@magic.io"}]
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 numberDiff line numberDiff 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 DownExpand 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='yury@magic.io',
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 numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 = "yury@magic.io"}]
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 numberDiff line numberDiff 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 DownExpand 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='yury@magic.io',
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 numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 = "yury@magic.io"}]
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 numberDiff line numberDiff 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 DownExpand 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='yury@magic.io',
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 numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 = "yury@magic.io"}]
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 numberDiff line numberDiff 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 DownExpand 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='yury@magic.io',
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 numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
skip_tests = False

import asyncio
import sys
import unittest
import weakref

Expand DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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