Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,13 @@ def _stable_abi(self) -> Optional[str]:
# not use the stable ABI filename suffix and wheels should not
# be tagged with the abi3 tag.
if self._limited_api and '__pypy__' not in sys.builtin_module_names:
# On free-threaded Python 3.15.0b2+, we expect to be
# building 'abi3t' wheels for the time being. In the future
# we will want an option to target 'abi3t' from GIL-enabled
# Python too.
abi3t = bool(sysconfig.get_config_var('Py_GIL_DISABLED')) and sys.version_info >= (3, 15)
expected_abi = 'abi3t' if abi3t else 'abi3'

# Verify stable ABI compatibility: examine files installed
# in {platlib} that look like extension modules, and raise
# an exception if any of them has a Python version
Expand All @@ -434,11 +441,11 @@ def _stable_abi(self) -> Optional[str]:
match = _EXTENSION_SUFFIX_REGEX.match(entry.dst.name)
if match:
abi = match.group('abi')
if abi is not None and abi != 'abi3':
if abi is not None and abi != expected_abi:
raise BuildError(
f'The package declares compatibility with Python limited API but extension '
f'module {os.fspath(entry.dst)!r} is tagged for a specific Python version.')
return 'abi3'
return 'abi3.abi3t' if abi3t else 'abi3'
return None

def _install_path(self, wheel_file: mesonpy._wheelfile.WheelFile, origin: Path, destination: pathlib.Path) -> None:
Expand Down Expand Up @@ -874,10 +881,10 @@ def __init__(
if not allow_limited_api:
self._limited_api = False

if self._limited_api and bool(sysconfig.get_config_var('Py_GIL_DISABLED')):
if self._limited_api and bool(sysconfig.get_config_var('Py_GIL_DISABLED')) and sys.version_info < (3, 15):
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.')
'The package targets Python\'s Limited API, which is not supported by free-threaded CPython before version '
'3.15. The "python.allow_limited_api" Meson build option may be used to override the package default.')

# Shared library support on Windows requires collaboration
# from the package, make sure the developers acknowledge this.
Expand Down
14 changes: 14 additions & 0 deletions tests/packages/limited-api-free-threaded/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT

project('limited-api-free-thraded', 'c', version: '1.0.0', meson_version: '>= 1.3')

py = import('python').find_installation(pure: false)

py.extension_module(
'module',
'module.c',
limited_api: '3.15',
install: true,
)
Comment thread
mgorny marked this conversation as resolved.
33 changes: 33 additions & 0 deletions tests/packages/limited-api-free-threaded/module.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2023 The meson-python developers
//
// SPDX-License-Identifier: MIT

#include <Python.h>

static PyObject* add(PyObject *self, PyObject *args) {
int a, b;

if (!PyArg_ParseTuple(args, "ii", &a, &b))
return NULL;

return PyLong_FromLong(a + b);
}

static PyMethodDef methods[] = {
{"add", add, METH_VARARGS, NULL},
{NULL, NULL, 0, NULL},
};

PyABIInfo_VAR(abi_info);

static PySlot module_slots[] = {
PySlot_STATIC_DATA(Py_mod_name, "module"),
PySlot_STATIC_DATA(Py_mod_methods, methods),
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
PySlot_DATA(Py_mod_abi, &abi_info),
PySlot_END,
};

PyMODEXPORT_FUNC PyModExport_module(void) {
return module_slots;
}
10 changes: 10 additions & 0 deletions tests/packages/limited-api-free-threaded/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2023 The meson-python developers
#
# SPDX-License-Identifier: MIT

[build-system]
build-backend = 'mesonpy'
requires = ['meson-python']

[tool.meson-python]
limited-api = true
2 changes: 1 addition & 1 deletion tests/packages/limited-api/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ static PyMethodDef methods[] = {

static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"plat",
"module",
NULL,
-1,
methods,
Expand Down
11 changes: 10 additions & 1 deletion tests/test_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import mesonpy._tags

from .conftest import adjust_packaging_platform_tag
from .test_wheel import NOGIL_BUILD


# Test against the wheel tag generated by packaging module.
Expand All @@ -29,6 +30,9 @@


def get_abi3_suffix():
# EXTENSION_SUFFIXES are ordered in preference order, and more specific ABI is preferred.
# On free-threaded 3.15+, this will match ".abi3t" as the only supported stable ABI.
# On GIL-enabled 3.15+, it will match plain ".abi3" first, since that ABI is more specific.
for suffix in importlib.machinery.EXTENSION_SUFFIXES:
if '.abi3' in suffix: # Unix
return suffix
Expand Down Expand Up @@ -130,7 +134,12 @@ def test_tag_stable_abi():
'platlib': [f'extension{ABI3SUFFIX}'],
}, limited_api=True)
# PyPy does not support the stable ABI.
abi = 'abi3' if '__pypy__' not in sys.builtin_module_names else ABI
if '__pypy__' in sys.builtin_module_names:
abi = ABI
elif NOGIL_BUILD and sys.version_info >= (3, 15):
abi = 'abi3.abi3t'
Comment thread
dnicolodi marked this conversation as resolved.
else:
abi = 'abi3'
assert str(builder.tag) == f'{INTERPRETER}-{abi}-{PLATFORM}'


Expand Down
11 changes: 11 additions & 0 deletions tests/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,3 +412,14 @@ def test_cmake_subproject(wheel_cmake_subproject):
f'.cmake_subproject.mesonpy.libs/libcmaketest{LIB_SUFFIX}',
f'cmakesubproject{EXT_SUFFIX}',
}


# Requires Meson 1.3.0, see https://github.com/mesonbuild/meson/pull/11745.
@pytest.mark.skipif(MESON_VERSION < (1, 2, 99), reason='meson too old')
@pytest.mark.skipif(sys.version_info < (3, 15), reason='Requires PEP 820 API, present since Python 3.15')
def test_limited_api_free_threaded(wheel_limited_api_free_threaded):
artifact = wheel.wheelfile.WheelFile(wheel_limited_api_free_threaded)
name = artifact.parsed_filename
assert name.group('pyver') == INTERPRETER
assert name.group('abi') == 'abi3.abi3t' if NOGIL_BUILD else 'abi3'
assert name.group('plat') == PLATFORM
Loading