From ce9982a61da3bf58f8b6948bc1b757ea0921995b Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Wed, 17 Apr 2024 20:57:10 +0200 Subject: [PATCH 1/2] MAINT: detect free-threaded CPython ("nogil") and handle limited API The free-threaded CPython build does not support the limited API yet, and won't in the near future. To avoid either cryptic build failures or a successfull build yielding an `abi3` tag which would not be correct, raise a clear error and don't attempt to build. --- mesonpy/__init__.py | 7 ++++++- tests/test_project.py | 2 +- tests/test_wheel.py | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py index feca54a4c..0455bfd19 100644 --- a/mesonpy/__init__.py +++ b/mesonpy/__init__.py @@ -747,7 +747,7 @@ def __init__( self._metadata.requires_python.prereleases = True if platform.python_version().rstrip('+') not in self._metadata.requires_python: raise MesonBuilderError( - f'Package requires Python version {self._metadata.requires_python}, ' + f'The package requires Python version {self._metadata.requires_python}, ' f'running on {platform.python_version()}') # limited API @@ -759,6 +759,11 @@ def __init__( if not value: self._limited_api = False + if self._limited_api and bool(sysconfig.get_config_var('Py_GIL_DISABLED')): + raise BuildError( + 'The package targets Python\'s Limited API, which is not supported by free-threaded CPython. ' + 'The "python.allow_limited_api" Meson build option may be used to override the package default.') + def _run(self, cmd: Sequence[str]) -> None: """Invoke a subprocess.""" # Flush the line to ensure that the log line with the executed diff --git a/tests/test_project.py b/tests/test_project.py index db56136d6..d2a4e5e47 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -23,7 +23,7 @@ def test_unsupported_python_version(package_unsupported_python_version): - with pytest.raises(mesonpy.MesonBuilderError, match='Package requires Python version ==1.0.0'): + with pytest.raises(mesonpy.MesonBuilderError, match='The package requires Python version ==1.0.0'): with mesonpy._project(): pass diff --git a/tests/test_wheel.py b/tests/test_wheel.py index 659ecbf5e..24ed59ffc 100644 --- a/tests/test_wheel.py +++ b/tests/test_wheel.py @@ -39,6 +39,8 @@ 'win32': '.dll', }.get(sys.platform, '.so') +NOGIL_BUILD = bool(sysconfig.get_config_var('Py_GIL_DISABLED')) + # Test against the wheel tag generated by packaging module. tag = next(packaging.tags.sys_tags()) ABI = tag.abi @@ -287,6 +289,7 @@ def test_skip_subprojects(package_subproject, tmp_path, arg): # Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745. @pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='Meson version too old') +@pytest.mark.skipif(NOGIL_BUILD, reason='Free-threaded CPython does not support the limited API') def test_limited_api(wheel_limited_api): artifact = wheel.wheelfile.WheelFile(wheel_limited_api) name = artifact.parsed_filename @@ -297,6 +300,7 @@ def test_limited_api(wheel_limited_api): # Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745. @pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='Meson version too old') +@pytest.mark.skipif(NOGIL_BUILD, reason='Free-threaded CPython does not support the limited API') @pytest.mark.xfail('__pypy__' in sys.builtin_module_names, reason='PyPy does not use special modules suffix for stable ABI') def test_limited_api_bad(package_limited_api, tmp_path): with pytest.raises(mesonpy.BuildError, match='The package declares compatibility with Python limited API but '): From 800eca7750ab786570fcdb0b7335059127a9ba19 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Wed, 17 Apr 2024 22:09:22 +0200 Subject: [PATCH 2/2] TST: skip test that fails under free-threaded CPython for too old Cython Support for free-threaded CPython in Cython is still a work in progress, see e.g. cython#6137 - this will land in Cython 3.1.0, and the skipped test does pass when using Cython built from the branch in cython#6137. --- tests/test_editable.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/test_editable.py b/tests/test_editable.py index 3121d688b..13f03f13d 100644 --- a/tests/test_editable.py +++ b/tests/test_editable.py @@ -6,6 +6,8 @@ import os import pathlib import pkgutil +import re +import subprocess import sys from contextlib import redirect_stdout @@ -16,7 +18,16 @@ from mesonpy import _editable -from .test_wheel import EXT_SUFFIX +from .test_wheel import EXT_SUFFIX, NOGIL_BUILD + + +def find_cython_version(): + cython_version_str = subprocess.run(['cython', '--version'], check=True, + stdout=subprocess.PIPE, text=True).stdout + version_str = re.search(r'(\d{1,4}\.\d{1,4}\.?\d{0,4})', cython_version_str).group(0) + return tuple(map(int, version_str.split('.'))) + +CYTHON_VERSION = find_cython_version() def test_walk(package_complex): @@ -280,6 +291,8 @@ def test_editable_rebuild(package_purelib_and_platlib, tmp_path, verbose, args): sys.modules.pop('pure', None) +@pytest.mark.skipif(NOGIL_BUILD and CYTHON_VERSION < (3, 1, 0), + reason='Cython version too old, no free-threaded CPython support') def test_editable_verbose(venv, package_complex, editable_complex, monkeypatch): monkeypatch.setenv('MESONPY_EDITABLE_VERBOSE', '1') venv.pip('install', os.fspath(editable_complex))