From 5e54e7f83911a37ea8be3b1480d75771dd3b8100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Sun, 21 Sep 2025 13:41:22 +0200 Subject: [PATCH 1/2] WHL: build limited-api compliant wheels --- .github/workflows/cibuildwheel.yml | 9 +++++++-- pyproject.toml | 1 + setup.py | 22 +++++++++++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index 999fdbc3..a9481b91 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -46,16 +46,21 @@ jobs: if: ${{ github.event_name }} == pull_request shell: bash # - On PPs, omit musllinux for speed - # - On PRs, run just oldest and newest Python versions + # - On PRs, run just oldest and newest Python versions (3.11 is the oldest abi3 target) run: | - CIBW_SKIP="cp310-win_arm64 cp311-* cp312-* cp313-* *musllinux*" + CIBW_SKIP="cp310-win_arm64 *musllinux*" echo "CIBW_SKIP=$CIBW_SKIP" >> $GITHUB_ENV echo "Setting CIBW_SKIP=$CIBW_SKIP" + CIBW_TEST_SKIP="cp312-* cp313-*" + echo "CIBW_TEST_SKIP=$CIBW_TEST_SKIP" >> $GITHUB_ENV + echo "Setting CIBW_TEST_SKIP=$CIBW_TEST_SKIP" + - name: "Building ${{ matrix.os }} (${{ matrix.arch }}) wheels" uses: pypa/cibuildwheel@v3.3.1 env: CIBW_SKIP: ${{ env.CIBW_SKIP }} + CIBW_TEST_SKIP: ${{ env.CIBW_TEST_SKIP }} CIBW_ARCHS: ${{ matrix.arch }} - uses: actions/upload-artifact@v6 diff --git a/pyproject.toml b/pyproject.toml index ae720e47..1f452b64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -127,3 +127,4 @@ test-command = [ ] manylinux-x86_64-image = "manylinux2014" manylinux-aarch64-image = "manylinux2014" +environment = { CFTIME_LIMITED_API = "1" } diff --git a/setup.py b/setup.py index eed7048f..8dab1020 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ import os import sys +import sysconfig import numpy from Cython.Build import cythonize @@ -29,6 +30,21 @@ CFTIME_DIR = os.path.join(SRCDIR, NAME) CYTHON_FNAME = os.path.join(CFTIME_DIR, '_{}.pyx'.format(NAME)) +USE_PY_LIMITED_API = ( + # require opt-in (builds are specialized by default) + os.getenv('CFTIME_LIMITED_API', '0') == '1' + # Cython + numpy + limited API de facto requires Python >=3.11 + and sys.version_info >= (3, 11) + # as of Python 3.14t, free-threaded builds don't support the limited API + and not sysconfig.get_config_var("Py_GIL_DISABLED") +) +ABI3_TARGET_VERSION = "".join(str(_) for _ in sys.version_info[:2]) +ABI3_TARGET_HEX = hex(sys.hexversion & 0xFFFF00F0) + +if USE_PY_LIMITED_API: + SETUP_OPTIONS = {"bdist_wheel": {"py_limited_api": f"cp{ABI3_TARGET_VERSION}"}} +else: + SETUP_OPTIONS = {} class CleanCython(Command): description = 'Purge artifacts built by Cython' @@ -60,6 +76,8 @@ def run(self): } DEFINE_MACROS += [('CYTHON_TRACE', '1'), ('CYTHON_TRACE_NOGIL', '1')] + if USE_PY_LIMITED_API: + DEFINE_MACROS.append(("Py_LIMITED_API", ABI3_TARGET_HEX)) if FLAG_COVERAGE in sys.argv: sys.argv.remove(FLAG_COVERAGE) print('enable: "linetrace" Cython compiler directive') @@ -71,7 +89,8 @@ def run(self): extension = Extension('{}._{}'.format(NAME, NAME), sources=[os.path.relpath(CYTHON_FNAME, BASEDIR)], define_macros=DEFINE_MACROS, - include_dirs=[numpy.get_include(),]) + include_dirs=[numpy.get_include()], + py_limited_api=USE_PY_LIMITED_API) ext_modules = cythonize( extension, @@ -82,4 +101,5 @@ def run(self): setup( cmdclass={'clean_cython': CleanCython}, ext_modules=ext_modules, + options=SETUP_OPTIONS, ) From 3870ccee9b9a35a88da67fa0b3d4a4ba696270a9 Mon Sep 17 00:00:00 2001 From: Filipe Fernandes Date: Fri, 27 Feb 2026 15:34:14 -0300 Subject: [PATCH 2/2] move macro definition out of the trace if-clause --- setup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 8dab1020..48371c37 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,9 @@ ABI3_TARGET_VERSION = "".join(str(_) for _ in sys.version_info[:2]) ABI3_TARGET_HEX = hex(sys.hexversion & 0xFFFF00F0) +if USE_PY_LIMITED_API: + DEFINE_MACROS += [(("Py_LIMITED_API", ABI3_TARGET_HEX))] + if USE_PY_LIMITED_API: SETUP_OPTIONS = {"bdist_wheel": {"py_limited_api": f"cp{ABI3_TARGET_VERSION}"}} else: @@ -76,8 +79,7 @@ def run(self): } DEFINE_MACROS += [('CYTHON_TRACE', '1'), ('CYTHON_TRACE_NOGIL', '1')] - if USE_PY_LIMITED_API: - DEFINE_MACROS.append(("Py_LIMITED_API", ABI3_TARGET_HEX)) + if FLAG_COVERAGE in sys.argv: sys.argv.remove(FLAG_COVERAGE) print('enable: "linetrace" Cython compiler directive')