Skip to content

Commit 11ad720

Browse files
authored
Merge branch 'master' into ssl_optimizations
2 parents afe7e60 + 3060ceb commit 11ad720

File tree

14 files changed

+280
-96
lines changed

14 files changed

+280
-96
lines changed

.github/workflows/release.yml

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ jobs:
3737
mkdir -p dist/
3838
echo "${VERSION}" > dist/VERSION
3939
40-
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
40+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
4141
with:
42-
name: dist
42+
name: dist-version
4343
path: dist/
4444

4545
build-sdist:
@@ -55,7 +55,7 @@ jobs:
5555
fetch-depth: 50
5656
submodules: true
5757

58-
- uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
58+
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
5959
with:
6060
python-version: 3.x
6161

@@ -64,9 +64,9 @@ jobs:
6464
python -m pip install --upgrade setuptools wheel pip
6565
python setup.py sdist
6666
67-
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
67+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
6868
with:
69-
name: dist
69+
name: dist-sdist
7070
path: dist/*.tar.*
7171

7272
build-wheels:
@@ -75,20 +75,28 @@ jobs:
7575
strategy:
7676
fail-fast: false
7777
matrix:
78-
os: [ubuntu-latest, macos-latest]
79-
cibw_python:
80-
- "cp38-*"
81-
- "cp39-*"
82-
- "cp310-*"
83-
- "cp311-*"
84-
- "cp312-*"
85-
- "cp313-*"
78+
os: [ubuntu-latest, macos-latest, ubuntu-22.04-arm]
79+
python:
80+
- "cp38"
81+
- "cp39"
82+
- "cp310"
83+
- "cp311"
84+
- "cp312"
85+
- "cp313"
86+
- "cp314"
87+
- "cp314t"
8688
cibw_arch: ["x86_64", "aarch64", "universal2"]
8789
exclude:
8890
- os: ubuntu-latest
8991
cibw_arch: universal2
92+
- os: ubuntu-latest
93+
cibw_arch: aarch64
9094
- os: macos-latest
9195
cibw_arch: aarch64
96+
- os: ubuntu-22.04-arm
97+
cibw_arch: x86_64
98+
- os: ubuntu-22.04-arm
99+
cibw_arch: universal2
92100

93101
defaults:
94102
run:
@@ -103,27 +111,21 @@ jobs:
103111
fetch-depth: 50
104112
submodules: true
105113

106-
- name: Set up QEMU
107-
if: matrix.os == 'ubuntu-latest' && matrix.cibw_arch == 'aarch64'
108-
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
109-
with:
110-
platforms: arm64
111-
112114
- name: Install macOS deps
113115
if: startsWith(matrix.os, 'macos')
114116
run: |
115117
brew install gnu-sed libtool autoconf automake
116118
117-
- uses: pypa/cibuildwheel@bd033a44476646b606efccdd5eed92d5ea1d77ad # v2.20.0
119+
- uses: pypa/cibuildwheel@7c619efba910c04005a835b110b057fc28fd6e93 # v3.2.0
118120
env:
119121
CIBW_BUILD_VERBOSITY: 1
120-
CIBW_BUILD: ${{ matrix.cibw_python }}
122+
CIBW_BUILD: ${{ matrix.python }}-*
121123
CIBW_ARCHS: ${{ matrix.cibw_arch }}
122124
CIBW_TEST_SKIP: "*universal2:arm64"
123125

124-
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
126+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
125127
with:
126-
name: dist
128+
name: dist-wheels-${{ matrix.os }}-${{ matrix.python }}-${{ matrix.cibw_arch }}
127129
path: wheelhouse/*.whl
128130

129131
publish:
@@ -136,9 +138,10 @@ jobs:
136138
fetch-depth: 5
137139
submodules: false
138140

139-
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
141+
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
140142
with:
141-
name: dist
143+
pattern: dist-*
144+
merge-multiple: true
142145
path: dist/
143146

144147
- name: Extract Release Version
@@ -172,7 +175,7 @@ jobs:
172175
ls -al dist/
173176
174177
- name: Upload to PyPI
175-
uses: pypa/gh-action-pypi-publish@b7f401de30cb6434a1e19f805ff006643653240e # v1.8.10
178+
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
176179
with:
177180
user: __token__
178181
password: ${{ secrets.PYPI_TOKEN }}

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ jobs:
2222
- "3.11"
2323
- "3.12"
2424
- "3.13"
25+
- "3.14"
26+
- "3.14t"
2527
os: [ubuntu-latest, macos-latest]
2628

2729
env:
@@ -44,7 +46,7 @@ jobs:
4446
__version__\s*=\s*(?:['"])([[:PEP440:]])(?:['"])
4547
4648
- name: Set up Python ${{ matrix.python-version }}
47-
uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
49+
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
4850
if: steps.release.outputs.version == 0
4951
with:
5052
python-version: ${{ matrix.python-version }}
@@ -57,8 +59,6 @@ jobs:
5759
5860
- name: Install Python Deps
5961
if: steps.release.outputs.version == 0
60-
env:
61-
PIP_PRE: ${{ matrix.python-version == '3.13' && '1' || '0' }}
6262
run: |
6363
pip install -e .[test,dev]
6464

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "uvloop"
33
description = "Fast implementation of asyncio event loop on top of libuv"
44
authors = [{name = "Yury Selivanov", email = "yury@magic.io"}]
5-
requires-python = '>=3.8.0'
5+
requires-python = '>=3.8.1'
66
readme = "README.rst"
77
license = {text = "MIT License"}
88
dynamic = ["version"]
@@ -38,10 +38,10 @@ test = [
3838
# their combination breaks too often
3939
# (example breakage: https://gitlab.com/pycqa/flake8/issues/427)
4040
'aiohttp>=3.10.5',
41-
'flake8~=5.0',
41+
'flake8~=6.1',
4242
'psutil',
43-
'pycodestyle~=2.9.0',
44-
'pyOpenSSL~=23.0.0',
43+
'pycodestyle~=2.11.0',
44+
'pyOpenSSL~=25.3.0',
4545
'mypy>=0.800',
4646
]
4747
dev = [
@@ -58,7 +58,7 @@ docs = [
5858
requires = [
5959
"setuptools>=60",
6060
"wheel",
61-
"Cython~=3.0",
61+
"Cython~=3.1",
6262
]
6363
build-backend = "setuptools.build_meta"
6464

tests/test_base.py

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import random
66
import sys
7+
import subprocess
78
import threading
89
import time
910
import uvloop
@@ -576,9 +577,14 @@ def get_name(self):
576577
async def coro():
577578
pass
578579

579-
factory = lambda loop, coro, **kwargs: MyTask(
580-
coro, loop=loop, **kwargs
581-
)
580+
def factory(loop, coro, **kwargs):
581+
task = MyTask(coro, loop=loop, **kwargs)
582+
# Python moved the responsibility to set the name to the Task
583+
# class constructor, so MyTask.set_name is never called by
584+
# Python's create_task. Compensate for that here.
585+
if self.is_asyncio_loop() and "name" in kwargs:
586+
task.set_name(kwargs["name"])
587+
return task
582588

583589
self.assertIsNone(self.loop.get_task_factory())
584590
task = self.loop.create_task(coro(), name="mytask")
@@ -730,6 +736,69 @@ def scheduler():
730736
thread.join()
731737
self.assertEqual(counter[0], ITERATIONS)
732738

739+
def test_freethreading(self):
740+
if not hasattr(sys, "_is_gil_enabled"):
741+
raise unittest.SkipTest("No sys._is_gil_enabled()")
742+
if os.cpu_count() < 2:
743+
raise unittest.SkipTest("Flaky on single CPU machines")
744+
prog = """\
745+
import asyncio
746+
import os
747+
import sys
748+
import threading
749+
import time
750+
751+
752+
counter = 0
753+
754+
755+
def job(barrier):
756+
global counter
757+
barrier.wait()
758+
start_time = time.monotonic()
759+
rv = 0
760+
while time.monotonic() - start_time < 1:
761+
for _i in range(10**4):
762+
counter += 1
763+
rv += 1
764+
return rv
765+
766+
767+
async def main():
768+
if sys._is_gil_enabled():
769+
print("{impl} turned on GIL")
770+
return False
771+
loop = asyncio.get_running_loop()
772+
n_jobs = os.cpu_count()
773+
barrier = threading.Barrier(n_jobs)
774+
fs = [loop.run_in_executor(None, job, barrier) for _ in range(n_jobs)]
775+
result = sum(await asyncio.gather(*fs))
776+
if counter == result:
777+
print("Expected race condition did not happen")
778+
return False
779+
return True
780+
781+
782+
if __name__ == "__main__":
783+
if sys._is_gil_enabled():
784+
print("Not running with GIL disabled")
785+
sys.exit(2)
786+
787+
import {impl}
788+
789+
if not {impl}.run(main()):
790+
sys.exit(1)
791+
"""
792+
result = subprocess.run(
793+
[sys.executable, '-c', prog.format(impl=self.implementation)],
794+
stdout=subprocess.PIPE,
795+
text=True,
796+
)
797+
if result.returncode == 2:
798+
raise unittest.SkipTest(result.stdout.strip())
799+
elif result.returncode != 0:
800+
self.fail(result.stdout.strip())
801+
733802

734803
class TestBaseUV(_TestBase, UVTestCase):
735804

tests/test_dns.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ def patched_getaddrinfo(*args, **kwargs):
1010
# flag AI_CANONNAME, even if `host` is an IP
1111
rv = []
1212
result = socket.getaddrinfo(*args, **kwargs)
13+
first = True
1314
for af, sk, proto, canon_name, addr in result:
1415
if kwargs.get('flags', 0) & socket.AI_CANONNAME:
15-
if not canon_name:
16+
if not canon_name and first:
17+
first = False
1618
canon_name = args[0]
1719
if not isinstance(canon_name, str):
1820
canon_name = canon_name.decode('ascii')
@@ -24,7 +26,7 @@ def patched_getaddrinfo(*args, **kwargs):
2426

2527
class BaseTestDNS:
2628

27-
def _test_getaddrinfo(self, *args, _patch=False, **kwargs):
29+
def _test_getaddrinfo(self, *args, _patch=False, _sorted=False, **kwargs):
2830
err = None
2931
try:
3032
if _patch:
@@ -50,7 +52,18 @@ def _test_getaddrinfo(self, *args, _patch=False, **kwargs):
5052
if err is not None:
5153
raise err
5254

53-
self.assertEqual(a1, a2)
55+
if _sorted:
56+
if kwargs.get('flags', 0) & socket.AI_CANONNAME and a1 and a2:
57+
# The API doesn't guarantee the ai_canonname value if
58+
# multiple results are returned, but both implementations
59+
# must return the same value for the first result.
60+
self.assertEqual(a1[0][3], a2[0][3])
61+
a1 = [(af, sk, pr, addr) for af, sk, pr, _, addr in a1]
62+
a2 = [(af, sk, pr, addr) for af, sk, pr, _, addr in a2]
63+
64+
self.assertEqual(sorted(a1), sorted(a2))
65+
else:
66+
self.assertEqual(a1, a2)
5467

5568
def _test_getnameinfo(self, *args, **kwargs):
5669
err = None
@@ -77,11 +90,13 @@ def _test_getnameinfo(self, *args, **kwargs):
7790
self.assertEqual(a1, a2)
7891

7992
def test_getaddrinfo_1(self):
80-
self._test_getaddrinfo('example.com', 80)
81-
self._test_getaddrinfo('example.com', 80, type=socket.SOCK_STREAM)
93+
self._test_getaddrinfo('example.com', 80, _sorted=True)
94+
self._test_getaddrinfo('example.com', 80, type=socket.SOCK_STREAM,
95+
_sorted=True)
8296

8397
def test_getaddrinfo_2(self):
84-
self._test_getaddrinfo('example.com', 80, flags=socket.AI_CANONNAME)
98+
self._test_getaddrinfo('example.com', 80, flags=socket.AI_CANONNAME,
99+
_sorted=True)
85100

86101
def test_getaddrinfo_3(self):
87102
self._test_getaddrinfo('a' + '1' * 50 + '.wat', 800)
@@ -92,12 +107,14 @@ def test_getaddrinfo_4(self):
92107
family=-1)
93108

94109
def test_getaddrinfo_5(self):
95-
self._test_getaddrinfo('example.com', '80')
96-
self._test_getaddrinfo('example.com', '80', type=socket.SOCK_STREAM)
110+
self._test_getaddrinfo('example.com', '80', _sorted=True)
111+
self._test_getaddrinfo('example.com', '80', type=socket.SOCK_STREAM,
112+
_sorted=True)
97113

98114
def test_getaddrinfo_6(self):
99-
self._test_getaddrinfo(b'example.com', b'80')
100-
self._test_getaddrinfo(b'example.com', b'80', type=socket.SOCK_STREAM)
115+
self._test_getaddrinfo(b'example.com', b'80', _sorted=True)
116+
self._test_getaddrinfo(b'example.com', b'80', type=socket.SOCK_STREAM,
117+
_sorted=True)
101118

102119
def test_getaddrinfo_7(self):
103120
self._test_getaddrinfo(None, 0)
@@ -116,8 +133,9 @@ def test_getaddrinfo_10(self):
116133
self._test_getaddrinfo(None, None, type=socket.SOCK_STREAM)
117134

118135
def test_getaddrinfo_11(self):
119-
self._test_getaddrinfo(b'example.com', '80')
120-
self._test_getaddrinfo(b'example.com', '80', type=socket.SOCK_STREAM)
136+
self._test_getaddrinfo(b'example.com', '80', _sorted=True)
137+
self._test_getaddrinfo(b'example.com', '80', type=socket.SOCK_STREAM,
138+
_sorted=True)
121139

122140
def test_getaddrinfo_12(self):
123141
# musl always returns ai_canonname but we don't
@@ -199,6 +217,10 @@ def test_getaddrinfo_22(self):
199217
self._test_getaddrinfo(payload, 80)
200218
self._test_getaddrinfo(payload, 80, type=socket.SOCK_STREAM)
201219

220+
def test_getaddrinfo_broadcast(self):
221+
self._test_getaddrinfo('<broadcast>', 80)
222+
self._test_getaddrinfo('<broadcast>', 80, type=socket.SOCK_STREAM)
223+
202224
######
203225

204226
def test_getnameinfo_1(self):

tests/test_process.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,12 @@ async def cancel_make_transport():
685685
self.loop.run_until_complete(cancel_make_transport())
686686

687687
def test_cancel_post_init(self):
688+
if sys.version_info >= (3, 13) and self.implementation == 'asyncio':
689+
# https://github.com/python/cpython/issues/103847#issuecomment-3736561321
690+
# This test started to flake on CPython 3.13 and later,
691+
# so we skip it for asyncio tests until the issue is resolved.
692+
self.skipTest('flaky test on CPython 3.13+')
693+
688694
async def cancel_make_transport():
689695
coro = self.loop.subprocess_exec(asyncio.SubprocessProtocol,
690696
*self.PROGRAM_BLOCKED)

0 commit comments

Comments
 (0)