diff --git a/newsfragments/5367.bugfix.rst b/newsfragments/5367.bugfix.rst new file mode 100644 index 0000000000..b4242d7ed9 --- /dev/null +++ b/newsfragments/5367.bugfix.rst @@ -0,0 +1 @@ +Fixed a regression introduced in v84.2.0 that produced unimportable PyPy extension modules on Windows when ``py_limited_api=True`` by preserving PyPy's ABI-tagged extension suffix. diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 0f797c270b..2d37390966 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -80,7 +80,8 @@ def get_abi3_suffix(): suffix = get_config_var('EXT_SUFFIX') if not isinstance(suffix, str) or get_config_var('HAVE_DYNAMIC_LOADING') == 0: return None - if suffix.endswith('.pyd') and not suffix.startswith('_d'): + # PyPy also uses .pyd on Windows, but does not support CPython's stable ABI. + if suffix == '.pyd' or (suffix.startswith('.cp') and suffix.endswith('.pyd')): return '.pyd' if not suffix.startswith('.cpython-'): return None diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index a537e1b9a9..6a976a0ba9 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -63,7 +63,8 @@ ({'EXT_SUFFIX': '_d.cp315-win_amd64.pyd'}, None), ({'EXT_SUFFIX': '_d.pyd'}, None), ({'EXT_SUFFIX': '.pypy310-pp73-x86_64-linux-gnu.so'}, None), - ({'EXT_SUFFIX': '.pypy310-pp73-win_amd64.pyd'}, '.pyd'), + ({'EXT_SUFFIX': '.pypy310-pp73-win_amd64.pyd'}, None), + ({'EXT_SUFFIX': '.pypy311-pp80-win_amd64.pyd'}, None), ({'EXT_SUFFIX': '.dll'}, None), ( {'EXT_SUFFIX': '.cpython-312-x86_64-cygwin.dll', 'MACHDEP': 'cygwin'}, @@ -155,6 +156,27 @@ def test_abi3_filename_cross(self, monkeypatch, abi_flags): 'spam', 'normal' + suffix ) + @pytest.mark.parametrize( + 'suffix', + [ + '.pypy310-pp73-win_amd64.pyd', + '.pypy311-pp80-win_amd64.pyd', + '.pypy310-pp73-x86_64-linux-gnu.so', + '.pypy311-pp80-x86_64-linux-gnu.so', + ], + ) + @pytest.mark.parametrize('limited_api', [False, True]) + def test_pypy_ignores_limited_api(self, monkeypatch, suffix, limited_api): + """PyPy requires its own suffix even if an extension requests limited API.""" + monkeypatch.delenv('SETUPTOOLS_EXT_SUFFIX', raising=False) + monkeypatch.setitem(get_config_vars(), 'EXT_SUFFIX', suffix) + extension = Extension('spam.eggs', ['eggs.c'], py_limited_api=limited_api) + cmd = build_ext(Distribution(dict(ext_modules=[extension]))) + cmd.finalize_options() + assert cmd.get_ext_filename('spam.eggs') == os.path.join( + 'spam', 'eggs' + suffix + ) + def test_ext_suffix_override(self): """ SETUPTOOLS_EXT_SUFFIX variable always overrides