From a2605b297c147a1ad54078181d32fe369fa4f37d Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Wed, 3 Aug 2016 21:55:39 -0400 Subject: use abi3 extension if Extension().is_abi3 --- setuptools/command/build_ext.py | 13 +++++++++++++ setuptools/extension.py | 4 ++++ setuptools/tests/test_build_ext.py | 20 +++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index e6db0764..dad28999 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -59,6 +59,14 @@ elif os.name != 'nt': if_dl = lambda s: s if have_rtld else '' +def get_abi3_suffix(): + """Return the file extension for an abi3-compliant Extension()""" + import imp + for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION): + if '.abi3' in suffix: # Unix + return suffix + elif suffix == '.pyd': # Windows + return suffix class build_ext(_build_ext): @@ -96,6 +104,11 @@ class build_ext(_build_ext): filename = _build_ext.get_ext_filename(self, fullname) if fullname in self.ext_map: ext = self.ext_map[fullname] + if sys.version_info[0] != 2 and getattr(ext, 'is_abi3'): + from distutils.sysconfig import get_config_var + so_ext = get_config_var('SO') + filename = filename[:-len(so_ext)] + filename = filename + get_abi3_suffix() if isinstance(ext, Library): fn, ext = os.path.splitext(filename) return self.shlib_compiler.library_filename(fn, libtype) diff --git a/setuptools/extension.py b/setuptools/extension.py index 5ea72c06..a6cc0915 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -36,6 +36,10 @@ have_pyrex = _have_cython class Extension(_Extension): """Extension that uses '.c' files in place of '.pyx' files""" + def __init__(self, name, sources, is_abi3=False, **kw): + self.is_abi3 = is_abi3 + _Extension.__init__(self, name, sources, **kw) + def _convert_pyx_sources_to_lang(self): """ Replace sources with .pyx extensions to sources with the target diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 5168ebf0..e0f2e73b 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -1,8 +1,10 @@ +import sys import distutils.command.build_ext as orig +from distutils.sysconfig import get_config_var from setuptools.command.build_ext import build_ext from setuptools.dist import Distribution - +from setuptools.extension import Extension class TestBuildExt: @@ -18,3 +20,19 @@ class TestBuildExt: res = cmd.get_ext_filename('foo') wanted = orig.build_ext.get_ext_filename(cmd, 'foo') assert res == wanted + + def test_abi3_filename(self): + """ + Filename needs to be loadable by several versions + of Python 3 if 'is_abi3' is truthy on Extension() + """ + dist = Distribution(dict(ext_modules=[Extension('spam.eggs', [], is_abi3=True)])) + cmd = build_ext(dist) + res = cmd.get_ext_filename('spam.eggs') + + if sys.version_info[0] == 2: + assert res.endswith(get_config_var('SO')) + elif sys.platform == 'win32': + assert res.endswith('eggs.pyd') + else: + assert 'abi3' in res \ No newline at end of file -- cgit v1.2.3 From dab253cb72eb7c098393f985e32585e079607f66 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 5 Aug 2016 07:45:09 -0400 Subject: call finalize_options in test --- setuptools/tests/test_build_ext.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index e0f2e73b..c71aadca 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -2,7 +2,7 @@ import sys import distutils.command.build_ext as orig from distutils.sysconfig import get_config_var -from setuptools.command.build_ext import build_ext +from setuptools.command.build_ext import build_ext, get_abi3_suffix from setuptools.dist import Distribution from setuptools.extension import Extension @@ -26,8 +26,13 @@ class TestBuildExt: Filename needs to be loadable by several versions of Python 3 if 'is_abi3' is truthy on Extension() """ - dist = Distribution(dict(ext_modules=[Extension('spam.eggs', [], is_abi3=True)])) + print(get_abi3_suffix()) + + extension = Extension('spam.eggs', ['eggs.c'], is_abi3=True) + dist = Distribution(dict(ext_modules=[extension])) cmd = build_ext(dist) + cmd.finalize_options() + assert 'spam.eggs' in cmd.ext_map res = cmd.get_ext_filename('spam.eggs') if sys.version_info[0] == 2: -- cgit v1.2.3 From 702a4277768f0781e3d0a4cf770d29621f7f2cc3 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 5 Aug 2016 07:55:57 -0400 Subject: rename is_abi3 to py_limited_api --- setuptools/command/build_ext.py | 4 +++- setuptools/extension.py | 4 ++-- setuptools/tests/test_build_ext.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index dad28999..7bb4d24c 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -104,7 +104,9 @@ class build_ext(_build_ext): filename = _build_ext.get_ext_filename(self, fullname) if fullname in self.ext_map: ext = self.ext_map[fullname] - if sys.version_info[0] != 2 and getattr(ext, 'is_abi3'): + if (sys.version_info[0] != 2 + and getattr(ext, 'py_limited_api') + and get_abi3_suffix()): from distutils.sysconfig import get_config_var so_ext = get_config_var('SO') filename = filename[:-len(so_ext)] diff --git a/setuptools/extension.py b/setuptools/extension.py index a6cc0915..c8e9bc7d 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -36,8 +36,8 @@ have_pyrex = _have_cython class Extension(_Extension): """Extension that uses '.c' files in place of '.pyx' files""" - def __init__(self, name, sources, is_abi3=False, **kw): - self.is_abi3 = is_abi3 + def __init__(self, name, sources, py_limited_api=False, **kw): + self.py_limited_api = py_limited_api _Extension.__init__(self, name, sources, **kw) def _convert_pyx_sources_to_lang(self): diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index c71aadca..100869f6 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -28,7 +28,7 @@ class TestBuildExt: """ print(get_abi3_suffix()) - extension = Extension('spam.eggs', ['eggs.c'], is_abi3=True) + extension = Extension('spam.eggs', ['eggs.c'], py_limited_api=True) dist = Distribution(dict(ext_modules=[extension])) cmd = build_ext(dist) cmd.finalize_options() -- cgit v1.2.3 From 0a3f850b3bc6c888f0f8810e453e664f9fd320f6 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 5 Aug 2016 08:01:58 -0400 Subject: gate test against get_abi3_suffix() --- setuptools/tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 100869f6..f2e1f59d 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -35,7 +35,7 @@ class TestBuildExt: assert 'spam.eggs' in cmd.ext_map res = cmd.get_ext_filename('spam.eggs') - if sys.version_info[0] == 2: + if sys.version_info[0] == 2 or not get_abi3_suffix(): assert res.endswith(get_config_var('SO')) elif sys.platform == 'win32': assert res.endswith('eggs.pyd') -- cgit v1.2.3 From fc6050ad4c1481be0a1aba1f056e76aa8be50039 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 19 Aug 2016 15:58:00 -0400 Subject: changelog --- CHANGES.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5a2b9928..3c4b118e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,14 @@ v25.1.3 * #714 and #704: Revert fix as it breaks other components downstream that can't handle unicode. See #709, #710, and #712. +* Add Extension(py_limited_api=True). When set to a truthy value, + that extension gets a filename apropriate for code using Py_LIMITED_API. + When used correctly this allows a single compiled extension to work on + all future versions of CPython 3. + The py_limited_api argument only controls the filename. To be + compatible with multiple versions of Python 3, the C extension + will also need to set -DPy_LIMITED_API=... and be modified to use + only the functions in the limited API. v25.1.2 ------- -- cgit v1.2.3