Skip to content

Commit 067febe

Browse files
authored
Merge branch 'master' into 562-set-thread-name-prefix
2 parents da5cb65 + 963a5f3 commit 067febe

25 files changed

Lines changed: 888 additions & 353 deletions

.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@7940a4c0e76eb2030e473a5f864f291f63ee879b # v2.21.3
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

examples/bench/echoclient.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44

55
import argparse
66
import concurrent.futures
7+
import multiprocessing
78
import socket
89
import ssl
910
import time
1011

1112

1213
if __name__ == '__main__':
14+
multiprocessing.set_start_method("fork")
15+
1316
parser = argparse.ArgumentParser()
1417
parser.add_argument('--msize', default=1000, type=int,
1518
help='message size in bytes')

pyproject.toml

Lines changed: 8 additions & 7 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,15 +38,16 @@ 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 = [
48+
'packaging>=20',
4849
'setuptools>=60',
49-
'Cython~=3.0',
50+
'Cython~=3.1',
5051
]
5152
docs = [
5253
'Sphinx~=4.1.2',
@@ -56,9 +57,9 @@ docs = [
5657

5758
[build-system]
5859
requires = [
60+
"packaging>=20",
5961
"setuptools>=60",
60-
"wheel",
61-
"Cython~=3.0",
62+
"Cython~=3.1",
6263
]
6364
build-backend = "setuptools.build_meta"
6465

setup.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import pathlib
1313
import platform
1414
import re
15+
import shlex
1516
import shutil
1617
import subprocess
1718
import sys
@@ -21,9 +22,9 @@
2122
from setuptools.command.sdist import sdist
2223

2324

24-
CYTHON_DEPENDENCY = 'Cython~=3.0'
25+
CYTHON_DEPENDENCY = 'Cython~=3.1'
2526
MACHINE = platform.machine()
26-
MODULES_CFLAGS = [os.getenv('UVLOOP_OPT_CFLAGS', '-O2')]
27+
MODULES_CFLAGS = shlex.split(os.getenv('UVLOOP_OPT_CFLAGS', '-O2'))
2728
_ROOT = pathlib.Path(__file__).parent
2829
LIBUV_DIR = str(_ROOT / 'vendor' / 'libuv')
2930
LIBUV_BUILD_DIR = str(_ROOT / 'build' / 'libuv-{}'.format(MACHINE))
@@ -108,7 +109,7 @@ def finalize_options(self):
108109
need_cythonize = True
109110

110111
if need_cythonize:
111-
import pkg_resources
112+
from packaging.requirements import Requirement
112113

113114
# Double check Cython presence in case setup_requires
114115
# didn't go into effect (most likely because someone
@@ -121,8 +122,8 @@ def finalize_options(self):
121122
'please install {} to compile uvloop from source'.format(
122123
CYTHON_DEPENDENCY))
123124

124-
cython_dep = pkg_resources.Requirement.parse(CYTHON_DEPENDENCY)
125-
if Cython.__version__ not in cython_dep:
125+
cython_dep = Requirement(CYTHON_DEPENDENCY)
126+
if not cython_dep.specifier.contains(Cython.__version__):
126127
raise RuntimeError(
127128
'uvloop requires {}, got Cython=={}'.format(
128129
CYTHON_DEPENDENCY, Cython.__version__

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_context.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,11 @@ def close():
474474
# send data
475475
await self.loop.run_in_executor(None,
476476
ssl_sock.send, b'hello')
477+
# After gh-105836 run_in_executor may resolve without
478+
# yielding. This is very noticeable when PYTHONASYNCIODEBUG
479+
# is set. Hence, we yield explicitly so that the sent data
480+
# can reach the SSL buffer before close/resume_reading.
481+
await asyncio.sleep(0)
477482
# schedule a proactive transport close which will trigger
478483
# the flushing process to retrieve the remaining data
479484
self.loop.call_soon(close)
@@ -512,9 +517,10 @@ async def write_over():
512517
proto.transport.write(b'q' * 16384)
513518
count += 1
514519
else:
515-
proto.transport.write(b'q' * 16384)
516520
proto.transport.set_write_buffer_limits(high=256, low=128)
517-
count += 1
521+
while not proto.transport.get_write_buffer_size():
522+
proto.transport.write(b'q' * 16384)
523+
count += 1
518524
return count
519525

520526
s = self.loop.run_in_executor(None, accept)

0 commit comments

Comments
 (0)