Skip to content

Commit f7c0547

Browse files
hongleifantix
andauthored
Add Python 3.15 to CI and fix test issues (#758)
* Fix epoll test cleanup on Python 3.15 * Add Python 3.15 and free-threaded builds to test matrix * Fix races in thread checks and subprocess tests * Add Python 3.15 and 3.15t release wheel builds --------- Co-authored-by: Fantix King <fantix.king@gmail.com>
1 parent e8efea4 commit f7c0547

7 files changed

Lines changed: 74 additions & 22 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# cryptography has no CPython 3.15t wheels yet. These dependencies are only
5+
# needed to build test dependencies inside the cibuildwheel container.
6+
if command -v apk >/dev/null 2>&1; then
7+
apk add --no-cache openssl-dev libffi-dev pkgconf
8+
else
9+
# manylinux_2_28 ships OpenSSL 1.1.1; cryptography needs OpenSSL 3.
10+
yum install -y openssl3-devel libffi-devel pkgconfig
11+
# EPEL names its pkg-config file openssl3.pc; the build looks for openssl.
12+
mkdir -p /usr/local/lib/pkgconfig
13+
ln -s /usr/lib64/pkgconfig/openssl3.pc /usr/local/lib/pkgconfig/openssl.pc
14+
fi

‎.github/workflows/release.yml‎

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ jobs:
7575
strategy:
7676
fail-fast: false
7777
matrix:
78-
os: [ubuntu-latest, macos-latest, ubuntu-22.04-arm]
78+
os:
79+
- ubuntu-latest
80+
- ubuntu-22.04-arm
81+
- macos-15-intel
82+
- macos-latest
7983
python:
8084
- "cp38"
8185
- "cp39"
@@ -85,17 +89,17 @@ jobs:
8589
- "cp313"
8690
- "cp314"
8791
- "cp314t"
88-
cibw_arch: ["x86_64", "aarch64", "universal2"]
89-
exclude:
92+
- "cp315"
93+
- "cp315t"
94+
include:
9095
- os: ubuntu-latest
91-
cibw_arch: universal2
92-
- os: ubuntu-latest
93-
cibw_arch: aarch64
94-
- os: macos-latest
95-
cibw_arch: aarch64
96-
- os: ubuntu-22.04-arm
9796
cibw_arch: x86_64
9897
- os: ubuntu-22.04-arm
98+
cibw_arch: aarch64
99+
# Use native OpenSSL when compiling x86_64 test dependencies.
100+
- os: macos-15-intel
101+
cibw_arch: x86_64
102+
- os: macos-latest
99103
cibw_arch: universal2
100104

101105
defaults:
@@ -104,6 +108,13 @@ jobs:
104108

105109
env:
106110
PIP_DISABLE_PIP_VERSION_CHECK: 1
111+
CIBW_BUILD_VERBOSITY: 1
112+
CIBW_BUILD: ${{ matrix.python }}-*
113+
CIBW_ARCHS: ${{ matrix.cibw_arch }}
114+
# Let tests inside Linux containers recognize GitHub CI.
115+
CIBW_ENVIRONMENT_PASS_LINUX: GITHUB_WORKFLOW
116+
# Exercise arm64 here; x86_64 wheels have their own test jobs.
117+
CIBW_TEST_SKIP: "*universal2:x86_64"
107118

108119
steps:
109120
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
@@ -114,14 +125,23 @@ jobs:
114125
- name: Install macOS deps
115126
if: startsWith(matrix.os, 'macos')
116127
run: |
117-
brew install gnu-sed libtool autoconf automake
128+
brew install gnu-sed libtool autoconf automake openssl@3
129+
echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV"
118130
131+
# cibuildwheel 4 added Python 3.15 but removed Python 3.8.
119132
- uses: pypa/cibuildwheel@7c619efba910c04005a835b110b057fc28fd6e93 # v3.2.0
133+
if: matrix.python == 'cp38'
134+
135+
- uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
136+
if: matrix.python != 'cp38'
120137
env:
121-
CIBW_BUILD_VERBOSITY: 1
122-
CIBW_BUILD: ${{ matrix.python }}-*
123-
CIBW_ARCHS: ${{ matrix.cibw_arch }}
124-
CIBW_TEST_SKIP: "*universal2:arm64"
138+
CIBW_BEFORE_ALL_LINUX: >-
139+
${{ matrix.python == 'cp315t' && 'bash {project}/.github/workflows/install-linux-test-deps.sh' || '' }}
140+
# Build universal2 wheels, but compile their test dependencies for arm64.
141+
CIBW_TEST_ENVIRONMENT_MACOS: >-
142+
${{ matrix.cibw_arch == 'universal2'
143+
&& 'ARCHFLAGS="-arch arm64" _PYTHON_HOST_PLATFORM="macosx-11.0-arm64"'
144+
|| '' }}
125145
126146
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
127147
with:

‎.github/workflows/tests.yml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ jobs:
2424
- "3.13"
2525
- "3.14"
2626
- "3.14t"
27+
- "3.15"
28+
- "3.15t"
2729
os: [ubuntu-latest, macos-latest]
2830

2931
env:
@@ -55,7 +57,8 @@ jobs:
5557
- name: Install macOS deps
5658
if: matrix.os == 'macos-latest' && steps.release.outputs.version == 0
5759
run: |
58-
brew install gnu-sed libtool autoconf automake
60+
brew install gnu-sed libtool autoconf automake openssl@3
61+
echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV"
5962
6063
- name: Install Python Deps
6164
if: steps.release.outputs.version == 0

‎pyproject.toml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ test = [
4141
'flake8~=6.1',
4242
'psutil',
4343
'pycodestyle~=2.11.0',
44-
'pyOpenSSL~=25.3.0',
44+
# pyOpenSSL 26.4 requires Python 3.9 or later.
45+
'pyOpenSSL~=25.3.0; python_version < "3.9"',
46+
'pyOpenSSL~=26.4.0; python_version >= "3.9"',
4547
'mypy>=0.800',
4648
]
4749
dev = [

‎tests/test_base.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,16 @@ def check_in_thread(loop, event, debug, create_loop, fut):
290290
def test_thread(loop, debug, create_loop=False):
291291
event = threading.Event()
292292
fut = asyncio.Future(loop=loop)
293-
loop.call_soon(event.set)
294293
args = (loop, event, debug, create_loop, fut)
295294
thread = threading.Thread(target=check_in_thread, args=args)
295+
296+
def run_thread():
297+
# Keep the loop running for the thread checks, but prevent
298+
# concurrent access to its non-thread-safe scheduling APIs.
299+
event.set()
300+
thread.join()
301+
302+
loop.call_soon(run_thread)
296303
thread.start()
297304
loop.run_until_complete(fut)
298305
thread.join()

‎tests/test_process.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -884,9 +884,10 @@ class TestAsyncio_AIO_Process(_AsyncioTests, tb.AIOTestCase):
884884
class Test_UV_Process_Delayed(tb.UVTestCase):
885885

886886
class TestProto:
887-
def __init__(self):
887+
def __init__(self, closed):
888888
self.lost = 0
889889
self.stages = []
890+
self.closed = closed
890891

891892
def connection_made(self, transport):
892893
self.stages.append(('CM', transport))
@@ -905,10 +906,11 @@ def process_exited(self):
905906
def connection_lost(self, exc):
906907
self.stages.append(('CL', self.lost, exc))
907908
self.lost += 1
909+
self.closed.set_result(None)
908910

909911
async def run_sub(self, **kwargs):
910912
return await self.loop.subprocess_shell(
911-
lambda: self.TestProto(),
913+
lambda: self.TestProto(self.loop.create_future()),
912914
'echo 1',
913915
**kwargs)
914916

@@ -962,7 +964,8 @@ def test_process_delayed_stdio__not_paused__no_stdin(self):
962964
stdin=None,
963965
stdout=subprocess.PIPE,
964966
stderr=subprocess.PIPE))
965-
self.loop.run_until_complete(transport._wait())
967+
# Process exit alone does not guarantee that pipe callbacks have run.
968+
self.loop.run_until_complete(asyncio.wait_for(proto.closed, 10))
966969
self.assertEqual(transport.get_returncode(), 0)
967970
self.assertIsNot(transport, None)
968971
self.assertEqual(

‎tests/test_sockets.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import os
23
import pickle
34
import select
45
import socket
@@ -272,7 +273,9 @@ def test_socket_sync_remove(self):
272273
# See https://github.com/MagicStack/uvloop/issues/61 for details
273274

274275
sock = socket.socket()
275-
epoll = select.epoll.fromfd(self.loop._get_backend_id())
276+
# Keep a separate descriptor for the backend epoll instance. The loop
277+
# owns its descriptor and closes it when the loop is closed.
278+
epoll = select.epoll.fromfd(os.dup(self.loop._get_backend_id()))
276279

277280
try:
278281
cb = lambda: None
@@ -288,8 +291,8 @@ def test_socket_sync_remove(self):
288291

289292
finally:
290293
sock.close()
291-
self.loop.close()
292294
epoll.close()
295+
self.loop.close()
293296

294297
def test_add_reader_or_writer_transport_fd(self):
295298
def assert_raises():

0 commit comments

Comments
 (0)