Skip to content

Commit 7ed073d

Browse files
ngoldbaumcolesburykumaraditya303
authored
Add support for the free-threaded build (#178)
* Initial support for free threaded Python (#1) * fix various errors and warnings seen on clang and gcc * use manylinux 2_28 on x86_64 (this will be the default in manylinux 3) * split CT_LAZY_FIELD_LIST into a new listing of ct_flags_mut flags * Add CT_UNDER_CONSTRUCTION to indicate that a type is being mutated * adjust assert * only set CT_UNDER_CONSTRUCTION during lazy init * Move CT_CUSTOM_FIELD_POS and CT_WITH_PACKED_CHANGE to ct_flags_mut * rearrange and add asserts in do_realize_lazy_struct * remove unhelpful comment (Kumar will fix this soon!) * replace ct_is_hidden with asserts * clarify comment * fix thread safety of _realize_c_struct_or_union * fix missing unlock * mark tests as thread unsafe * mark TestZIntegration as thread unsafe * fix headers to avoid duplicated definitions * fix thread safety of enum (#10) * fix thread safety of cffi (#13) Co-authored-by: Nathan Goldbaum <[email protected]> * add trove classifier * add missing CI jobs * back out unnecessary CI config changes * pin cibuildwheel<3 in CI config * reduce the number of pytest-run-parallel jobs * remove unnecessary thread_unsafe markers, add reasons for all others * expand whatsnew * add 3.13t pytest-run-parallel jobs * revert more unnecessary changes * disable gil in extension produced by verify tests * fail the tests if the GIL is enabled at tests completion * force realizing lazy structs in ffi_sizeof * remove out-of-date FIXME * tweak error * revert unnecessary change * Unconditionally clear weak refs in ctypdescr_dealloc to avoid race in CPython internals. see python/cpython#135607 * mark tests as thread-unsafe that might concurrently call dlclose on the same extension * apply fix from c02343c in a few more spots * check for lazy_field_list in force_lazy_struct * use atomic loads for opcodes in realize_c_type_or_func_now * address code review * remove skips * add thread safety note for realize_c_type_or_func * use atomics for ct_stuff * Simplify ct_stuff handling * use _CFFI_LOAD_OP * use one global cffi lock for thread safety * do not export dummy * skip * unconditionally heap-allocate _ffi_type output * move mutable flags to bitflags for ct_flags_mut field * fix refleak * Apply suggestions from code review Co-authored-by: Kumar Aditya <[email protected]> * apply matti's review comments * update version numbers * remove PYPY trove classifier * expire pointer conversion deprecation to fix tests * error on free-threaded Python 3.13 * drop 3.13t CI and wheel builds * drop cibuilwheel ~=3.0b1 * delete now-unnecessary CIBW_ENABLE env var --------- Co-authored-by: Sam Gross <[email protected]> Co-authored-by: Kumar Aditya <[email protected]>
1 parent 67a170d commit 7ed073d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+985
-322
lines changed

.github/workflows/ci.yaml

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,18 @@ jobs:
7676
- { spec: cp310-manylinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
7777
- { spec: cp311-manylinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
7878
- { spec: cp312-manylinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
79-
- { spec: cp313-manylinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
80-
- { spec: cp314-manylinux_x86_64, arch: x86_64, cibw_version: cibuildwheel~=3.0b1 }
81-
# FIXME: need to run tests with PYTHON_GIL=0 on this build to actually test sans-GIL, but breaks packaging tests that use the wrong `virtualenv` script wrapper
82-
- { spec: cp314t-manylinux_x86_64, skip_artifact_upload: 'true', cibw_version: cibuildwheel~=3.0b1 }
79+
- { spec: cp313-manylinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
80+
- { spec: cp314-manylinux_x86_64, arch: x86_64 }
81+
- { spec: cp314t-manylinux_x86_64, arch: x86_64 }
8382
8483
# x86_64 musllinux
8584
- { spec: cp39-musllinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
8685
- { spec: cp310-musllinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
8786
- { spec: cp311-musllinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
8887
- { spec: cp312-musllinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
8988
- { spec: cp313-musllinux_x86_64, arch: x86_64, omit: ${{ env.skip_ci_redundant_jobs }} }
90-
- { spec: cp314-musllinux_x86_64, arch: x86_64, cibw_version: cibuildwheel~=3.0b1 }
89+
- { spec: cp314-musllinux_x86_64, arch: x86_64 }
90+
- { spec: cp314t-musllinux_x86_64, arch: x86_64 }
9191
9292
# i686 manylinux
9393
- { spec: cp39-manylinux_i686, arch: i686, omit: ${{ env.skip_ci_redundant_jobs }} }
@@ -109,31 +109,35 @@ jobs:
109109
- { spec: cp311-manylinux_aarch64, arch: aarch64, omit: ${{ env.skip_ci_redundant_jobs }} }
110110
- { spec: cp312-manylinux_aarch64, arch: aarch64, omit: ${{ env.skip_ci_redundant_jobs }} }
111111
- { spec: cp313-manylinux_aarch64, arch: aarch64, omit: ${{ env.skip_ci_redundant_jobs }} }
112-
- { spec: cp314-manylinux_aarch64, arch: aarch64, cibw_version: cibuildwheel~=3.0b1 }
112+
- { spec: cp314-manylinux_aarch64, arch: aarch64 }
113+
- { spec: cp314t-manylinux_aarch64, arch: aarch64 }
113114
114115
# aarch64 musllinux
115116
- { spec: cp39-musllinux_aarch64, arch: aarch64, omit: ${{ env.skip_ci_redundant_jobs }} }
116117
- { spec: cp310-musllinux_aarch64, arch: aarch64, omit: ${{ env.skip_ci_redundant_jobs }} }
117118
- { spec: cp311-musllinux_aarch64, arch: aarch64, omit: ${{ env.skip_ci_redundant_jobs }} }
118119
- { spec: cp312-musllinux_aarch64, arch: aarch64, omit: ${{ env.skip_ci_redundant_jobs }} }
119120
- { spec: cp313-musllinux_aarch64, arch: aarch64, omit: ${{ env.skip_ci_redundant_jobs }} }
120-
- { spec: cp314-musllinux_aarch64, arch: aarch64, cibw_version: cibuildwheel~=3.0b1 }
121+
- { spec: cp314-musllinux_aarch64, arch: aarch64 }
122+
- { spec: cp314t-musllinux_aarch64, arch: aarch64 }
121123
122124
# ppc64le manylinux
123125
- { spec: cp39-manylinux_ppc64le, arch: ppc64le, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs }} }
124126
- { spec: cp310-manylinux_ppc64le, arch: ppc64le, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs || env.skip_ci_redundant_jobs }} }
125127
- { spec: cp311-manylinux_ppc64le, arch: ppc64le, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs || env.skip_ci_redundant_jobs }} }
126128
- { spec: cp312-manylinux_ppc64le, arch: ppc64le, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs || env.skip_ci_redundant_jobs }} }
127129
- { spec: cp313-manylinux_ppc64le, arch: ppc64le, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs || env.skip_ci_redundant_jobs }} }
128-
- { spec: cp314-manylinux_ppc64le, arch: ppc64le, omit: ${{ env.skip_slow_jobs }}, cibw_version: cibuildwheel~=3.0b1 }
130+
- { spec: cp314-manylinux_ppc64le, arch: ppc64le, omit: ${{ env.skip_slow_jobs }} }
131+
- { spec: cp314t-manylinux_ppc64le, arch: ppc64le, omit: ${{ env.skip_slow_jobs }} }
129132
130133
# s390x manylinux
131134
- { spec: cp39-manylinux_s390x, arch: s390x, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs }} }
132135
- { spec: cp310-manylinux_s390x, arch: s390x, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs || env.skip_ci_redundant_jobs }} }
133136
- { spec: cp311-manylinux_s390x, arch: s390x, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs || env.skip_ci_redundant_jobs }} }
134137
- { spec: cp312-manylinux_s390x, arch: s390x, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs || env.skip_ci_redundant_jobs }} }
135138
- { spec: cp313-manylinux_s390x, arch: s390x, test_args: '{package}/src/c', omit: ${{ env.skip_slow_jobs || env.skip_ci_redundant_jobs }} }
136-
- { spec: cp314-manylinux_s390x, arch: s390x, omit: ${{ env.skip_slow_jobs }}, cibw_version: cibuildwheel~=3.0b1 }
139+
- { spec: cp314-manylinux_s390x, arch: s390x, omit: ${{ env.skip_slow_jobs }} }
140+
- { spec: cp314t-manylinux_s390x, arch: s390x, omit: ${{ env.skip_slow_jobs }} }
137141
138142
linux:
139143
needs: [python_sdist, make_linux_matrix]
@@ -178,7 +182,6 @@ jobs:
178182
CIBW_MUSLLINUX_X86_64_IMAGE: ${{ matrix.musllinux_img || 'musllinux_1_2' }}
179183
CIBW_MUSLLINUX_I686_IMAGE: ${{ matrix.musllinux_img || 'musllinux_1_2' }}
180184
CIBW_MUSLLINUX_AARCH64_IMAGE: ${{ matrix.musllinux_img || 'musllinux_1_2' }}
181-
CIBW_ENABLE: cpython-prerelease cpython-freethreading
182185
CIBW_TEST_REQUIRES: pytest setuptools # 3.12+ no longer includes distutils, just always ensure setuptools is present
183186
CIBW_TEST_COMMAND: PYTHONUNBUFFERED=1 python -m pytest ${{ matrix.test_args || '{project}' }} # default to test all
184187
run: |
@@ -220,15 +223,17 @@ jobs:
220223
- { spec: cp311-macosx_x86_64, runs_on: [macos-13], omit: ${{ env.skip_ci_redundant_jobs }} }
221224
- { spec: cp312-macosx_x86_64, runs_on: [macos-13], omit: ${{ env.skip_ci_redundant_jobs }} }
222225
- { spec: cp313-macosx_x86_64, runs_on: [macos-13], omit: ${{ env.skip_ci_redundant_jobs }} }
223-
- { spec: cp314-macosx_x86_64, runs_on: [macos-13], cibw_version: cibuildwheel~=3.0b1 }
226+
- { spec: cp314-macosx_x86_64, runs_on: [macos-13] }
227+
- { spec: cp314t-macosx_x86_64, runs_on: [macos-13] }
224228
225229
# arm64 macos
226230
- { spec: cp39-macosx_arm64, deployment_target: '11.0', run_wrapper: 'arch -arm64 bash --noprofile --norc -eo pipefail {0}', omit: ${{ env.skip_ci_redundant_jobs }} }
227231
- { spec: cp310-macosx_arm64, deployment_target: '11.0', run_wrapper: 'arch -arm64 bash --noprofile --norc -eo pipefail {0}', omit: ${{ env.skip_ci_redundant_jobs}} }
228232
- { spec: cp311-macosx_arm64, deployment_target: '11.0', run_wrapper: 'arch -arm64 bash --noprofile --norc -eo pipefail {0}', omit: ${{ env.skip_ci_redundant_jobs }} }
229233
- { spec: cp312-macosx_arm64, deployment_target: '11.0', run_wrapper: 'arch -arm64 bash --noprofile --norc -eo pipefail {0}', omit: ${{ env.skip_ci_redundant_jobs }} }
230234
- { spec: cp313-macosx_arm64, deployment_target: '11.0', run_wrapper: 'arch -arm64 bash --noprofile --norc -eo pipefail {0}', omit: ${{ env.skip_ci_redundant_jobs }} }
231-
- { spec: cp314-macosx_arm64, deployment_target: '11.0', run_wrapper: 'arch -arm64 bash --noprofile --norc -eo pipefail {0}', cibw_version: cibuildwheel~=3.0b1 }
235+
- { spec: cp314-macosx_arm64, deployment_target: '11.0', run_wrapper: 'arch -arm64 bash --noprofile --norc -eo pipefail {0}' }
236+
- { spec: cp314t-macosx_arm64, deployment_target: '11.0', run_wrapper: 'arch -arm64 bash --noprofile --norc -eo pipefail {0}' }
232237
233238
macos:
234239
needs: [python_sdist, make_macos_matrix]
@@ -263,7 +268,6 @@ jobs:
263268
id: build
264269
env:
265270
CIBW_BUILD: ${{ matrix.spec }}
266-
CIBW_ENABLE: cpython-prerelease
267271
CIBW_TEST_REQUIRES: pytest setuptools
268272
CIBW_TEST_COMMAND: pip install pip --upgrade; cd {project}; PYTHONUNBUFFERED=1 pytest
269273
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.deployment_target || '10.13' }}
@@ -306,21 +310,24 @@ jobs:
306310
- { spec: cp311-win_amd64, omit: ${{ env.skip_ci_redundant_jobs }} }
307311
- { spec: cp312-win_amd64, omit: ${{ env.skip_ci_redundant_jobs }} }
308312
- { spec: cp313-win_amd64, omit: ${{ env.skip_ci_redundant_jobs }} }
309-
- { spec: cp314-win_amd64, cibw_version: cibuildwheel~=3.0b1 }
313+
- { spec: cp314-win_amd64 }
314+
- { spec: cp314t-win_amd64 }
310315
311316
# x86 windows
312317
- { spec: cp39-win32, omit: ${{ env.skip_ci_redundant_jobs }} }
313318
- { spec: cp310-win32, omit: ${{ env.skip_ci_redundant_jobs }} }
314319
- { spec: cp311-win32, omit: ${{ env.skip_ci_redundant_jobs }} }
315320
- { spec: cp312-win32, omit: ${{ env.skip_ci_redundant_jobs }} }
316321
- { spec: cp313-win32, omit: ${{ env.skip_ci_redundant_jobs }} }
317-
- { spec: cp314-win32, cibw_version: cibuildwheel~=3.0b1 }
322+
- { spec: cp314-win32 }
323+
- { spec: cp314t-win32 }
318324
319325
# arm64 windows
320-
- { spec: cp311-win_arm64, runs_on: windows-11-arm, omit: ${{ env.skip_ci_redundant_jobs }}, cibw_version: cibuildwheel~=3.0b1 }
321-
- { spec: cp312-win_arm64, runs_on: windows-11-arm, omit: ${{ env.skip_ci_redundant_jobs }}, cibw_version: cibuildwheel~=3.0b1 }
322-
- { spec: cp313-win_arm64, runs_on: windows-11-arm, omit: ${{ env.skip_ci_redundant_jobs }}, cibw_version: cibuildwheel~=3.0b1 }
323-
- { spec: cp314-win_arm64, runs_on: windows-11-arm, cibw_version: cibuildwheel~=3.0b1 }
326+
- { spec: cp311-win_arm64, runs_on: windows-11-arm, omit: ${{ env.skip_ci_redundant_jobs }} }
327+
- { spec: cp312-win_arm64, runs_on: windows-11-arm, omit: ${{ env.skip_ci_redundant_jobs }} }
328+
- { spec: cp313-win_arm64, runs_on: windows-11-arm, omit: ${{ env.skip_ci_redundant_jobs }} }
329+
- { spec: cp314-win_arm64, runs_on: windows-11-arm }
330+
- { spec: cp314t-win_arm64, runs_on: windows-11-arm }
324331
325332
windows:
326333
needs: [python_sdist, make_windows_matrix]
@@ -345,7 +352,6 @@ jobs:
345352
id: build
346353
env:
347354
CIBW_BUILD: ${{ matrix.spec }}
348-
CIBW_ENABLE: cpython-prerelease
349355
CIBW_TEST_REQUIRES: pytest setuptools
350356
CIBW_TEST_COMMAND: ${{ matrix.test_cmd || 'python -m pytest {package}/src/c' }}
351357
# FIXME: /testing takes ~45min on Windows and has some failures...
@@ -358,7 +364,7 @@ jobs:
358364
tar zxf cffi*.tar.gz --strip-components=1 -C cffi
359365
360366
python -m pip install --upgrade pip
361-
pip install "${{ matrix.cibw_version || 'cibuildwheel'}}"
367+
pip install "${{ matrix.cibw_version || 'cibuildwheel' }}"
362368
python -m cibuildwheel --output-dir dist cffi
363369
364370
echo "artifact_name=$(ls ./dist/)" >> "$GITHUB_OUTPUT"
@@ -384,10 +390,73 @@ jobs:
384390
delete-merged: true
385391
if: ${{ env.skip_artifact_upload != 'true' }}
386392

393+
make_run_parallel_matrix:
394+
runs-on: ubuntu-24.04
395+
outputs:
396+
matrix_json: ${{ steps.make_matrix.outputs.matrix_json }}
397+
steps:
398+
- uses: actions/checkout@v4
399+
- name: make a matrix
400+
id: make_matrix
401+
uses: ./.github/actions/dynamatrix
402+
with:
403+
matrix_yaml: |
404+
include:
405+
- { runner: ubuntu-latest, python-version: 3.14t-dev }
406+
- { runner: macos-latest, python-version: 3.14t-dev }
407+
- { runner: windows-latest, python-version: 3.14t-dev }
408+
409+
410+
pytest-run-parallel:
411+
needs: make_run_parallel_matrix
412+
strategy:
413+
fail-fast: false
414+
matrix: ${{ fromJSON(needs.make_run_parallel_matrix.outputs.matrix_json) }}
415+
416+
runs-on: ${{ matrix.runner }}
417+
steps:
418+
- name: clone repo
419+
uses: actions/checkout@v4
420+
421+
- name: install python
422+
uses: actions/setup-python@v5
423+
with:
424+
python-version: ${{ matrix.python-version }}
425+
426+
- name: build and install
427+
run: |
428+
python -m pip install pytest setuptools pytest-run-parallel
429+
python -m pip install .
430+
431+
- name: run tests under pytest-run-parallel
432+
if: runner.os == 'Windows'
433+
run: |
434+
python -m pytest --parallel-threads=4 src/c
435+
436+
- name: run tests under pytest-run-parallel
437+
if: runner.os != 'Windows'
438+
run: |
439+
python -m pytest --parallel-threads=4
440+
441+
clang_TSAN:
442+
runs-on: ubuntu-latest
443+
container: ghcr.io/nascheme/numpy-tsan:3.14t
444+
steps:
445+
- uses: actions/checkout@v4
446+
447+
- name: build and install
448+
run: |
449+
python -m pip install setuptools pytest pytest-run-parallel
450+
CFLAGS="-g -O3 -fsanitize=thread" python -m pip install -v .
451+
452+
- name: run tests under pytest-run-parallel
453+
run: |
454+
TSAN_OPTIONS="suppressions=$PWD/suppressions_free_threading.txt" \
455+
python -m pytest --parallel-threads=4 --skip-thread-unsafe=True -sv
387456
388457
check:
389458
if: always()
390-
needs: [python_sdist, linux, macos, windows, merge_artifacts]
459+
needs: [python_sdist, linux, macos, windows, clang_TSAN, pytest-run-parallel, merge_artifacts]
391460
runs-on: ubuntu-24.04
392461
steps:
393462
- name: Verify all previous jobs succeeded (provides a single check to sample for gating purposes)

doc/source/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
# built documents.
4646
#
4747
# The short X.Y version.
48-
version = '1.18'
48+
version = '2.0'
4949
# The full version, including alpha/beta/rc tags.
50-
release = '1.18.0.dev0'
50+
release = '2.0.0.dev0'
5151

5252
# The language for content autogenerated by Sphinx. Refer to documentation
5353
# for a list of supported languages.

doc/source/whatsnew.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@
22
What's New
33
======================
44

5-
v1.18.0.dev0
5+
v2.0.0.dev0
66
============
77

8+
* Added support for free threaded Python. (`#178`_)
9+
Note that the free-threaded build does not yet support building extensions
10+
with the limited API, so you must set py_limited_api=False when building
11+
extensions for the free-threaded build.
12+
* Added support for Python 3.14. (`#177`_)
813
* WIP
914

15+
.. _`#177`: https://github.com/python-cffi/cffi/pull/177
16+
.. _`#178`: https://github.com/python-cffi/cffi/pull/178
17+
1018
v1.17.1
1119
=======
1220

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
88

99
[project]
1010
name = "cffi"
11-
version = "1.18.0.dev0"
11+
version = "2.0.0.dev0"
1212
dependencies = [
1313
"pycparser; implementation_name != 'PyPy'",
1414
]
@@ -26,8 +26,8 @@ classifiers = [
2626
"Programming Language :: Python :: 3.12",
2727
"Programming Language :: Python :: 3.13",
2828
"Programming Language :: Python :: 3.14",
29+
"Programming Language :: Python :: Free Threading :: 2 - Beta",
2930
"Programming Language :: Python :: Implementation :: CPython",
30-
"Programming Language :: Python :: Implementation :: PyPy",
3131
]
3232
authors = [
3333
{name = "Armin Rigo"},

setup.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import sys, os, platform
1+
import sys, sysconfig, os, platform
22
import subprocess
33
import errno
44

@@ -16,6 +16,12 @@
1616
extra_compile_args = []
1717
extra_link_args = []
1818

19+
FREE_THREADED_BUILD = bool(sysconfig.get_config_var('Py_GIL_DISABLED'))
20+
21+
if FREE_THREADED_BUILD and sys.version_info < (3, 14):
22+
raise RuntimeError("CFFI does not support the free-threaded build of CPython 3.13. "
23+
"Upgrade to free-threaded 3.14 or newer to use CFFI with the "
24+
"free-threaded build.")
1925

2026
def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False):
2127
pkg_config = os.environ.get('PKG_CONFIG','pkg-config')

0 commit comments

Comments
 (0)