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
4 changes: 4 additions & 0 deletions newsfragments/5363.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fixed unimportable extensions on free-threaded Python 3.13 and 3.14 when
``Extension.py_limited_api`` is enabled. These builds now use their
version-specific extension suffix instead of using the unsupported ``.abi3t``
suffix.
4 changes: 4 additions & 0 deletions setuptools/command/build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ def get_abi3_suffix():
if get_config_var('DYNLOADFILE') == 'dynload_hpux.o':
return None

# The 3.13t and 3.14t loaders cannot import .abi3t files.
if suffix.startswith(('.cpython-313t', '.cpython-314t')):
return None

abi = '.abi3t' if get_config_var('Py_GIL_DISABLED') else '.abi3'
platform = get_config_var('SOABI_PLATFORM')
_, ext = os.path.splitext(suffix)
Expand Down
15 changes: 12 additions & 3 deletions setuptools/tests/test_bdist_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import shutil
import stat
import struct
import subprocess
import sys
import sysconfig
from contextlib import nullcontext, suppress
Expand All @@ -18,6 +19,7 @@
from packaging import tags

import setuptools
from setuptools.archive_util import unpack_zipfile
from setuptools.command.bdist_wheel import bdist_wheel, get_abi_tag, stable_abi_tag
from setuptools.command.build_ext import get_abi3_suffix
from setuptools.dist import Distribution
Expand Down Expand Up @@ -529,17 +531,24 @@ def test_limited_api(monkeypatch, tmp_path, tmp_path_factory):
@pytest.mark.filterwarnings(
"once:Config variable '.*' is unset.*, Python ABI tag may be incorrect"
)
def test_version_specific_abi(monkeypatch, tmp_path, tmp_path_factory):
"""Extensions not using the limited API get a version-specific ABI tag."""
@pytest.mark.parametrize('limited_api', [False, True])
def test_version_specific_abi(monkeypatch, tmp_path, tmp_path_factory, limited_api):
"""Version-specific wheels must import with or without py_limited_api."""
source_dir = tmp_path_factory.mktemp("extension_dist")
(source_dir / "setup.py").write_text(VERSION_SPECIFIC_SETUPPY, encoding="utf-8")
setup_py = EXTENSION_SETUPPY if limited_api else VERSION_SPECIFIC_SETUPPY
(source_dir / "setup.py").write_text(setup_py, encoding="utf-8")
(source_dir / "extension.c").write_text(EXTENSION_BODY, encoding="utf-8")
build_dir = tmp_path.joinpath("build")
dist_dir = tmp_path.joinpath("dist")
monkeypatch.chdir(source_dir)
bdist_wheel_cmd(bdist_dir=str(build_dir), dist_dir=str(dist_dir)).run()
wheel_path = next(dist_dir.glob("*.whl"))
assert f"-{get_abi_tag()}-" in wheel_path.name
installed = tmp_path / 'installed'
unpack_zipfile(wheel_path, installed)
subprocess.run(
[sys.executable, '-c', 'import extension'], cwd=installed, check=True
)


@pytest.mark.skipif(
Expand Down
7 changes: 4 additions & 3 deletions setuptools/tests/test_build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
({'Py_GIL_DISABLED': 1, 'VERSION': None}, '.abi3t.so'),
({'Py_GIL_DISABLED': 1, 'VERSION': '3.14'}, '.abi3t.so'),
({'Py_GIL_DISABLED': 1, 'VERSION': '3.13'}, '.abi3t.so'),
({'EXT_SUFFIX': '.cpython-313t-darwin.so', 'Py_GIL_DISABLED': 1}, None),
({'EXT_SUFFIX': '.cpython-314t-darwin.so', 'Py_GIL_DISABLED': 1}, None),
({'EXT_SUFFIX': '.cp315-win_amd64.pyd'}, '.pyd'),
({'EXT_SUFFIX': '.cp315-win_arm64.pyd'}, '.pyd'),
({'EXT_SUFFIX': '.cp310-win32.pyd'}, '.pyd'),
Expand Down Expand Up @@ -169,11 +171,10 @@ def test_ext_suffix_override(self):
# Mock value needed to pass tests
ext._links_to_dynamic = False

if not IS_PYPY:
if not IS_PYPY and get_abi3_suffix():
expect = cmd.get_ext_filename('for_abi3')
else:
# PyPy builds do not use ABI3 tag, so they will
# also get the overridden suffix.
# Builds without an ABI3 tag also get the overridden suffix.
expect = 'for_abi3.test-suffix'

try:
Expand Down
Loading