From ec6b7536d9c7714b4a000ace322ebeefe5a7f9b9 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Sat, 20 Aug 2016 19:47:34 -0400 Subject: add py-limited-api flag to control abi3 wheel tagging --- docs/index.rst | 18 +++++++++++++++--- wheel/bdist_wheel.py | 25 +++++++++++++++++++++---- wheel/pep425tags.py | 11 ++++++++++- wheel/test/extension.dist/extension.c | 2 ++ wheel/test/extension.dist/setup.cfg | 2 ++ wheel/test/extension.dist/setup.py | 20 ++++++++++++++++++++ 6 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 wheel/test/extension.dist/extension.c create mode 100644 wheel/test/extension.dist/setup.cfg create mode 100644 wheel/test/extension.dist/setup.py diff --git a/docs/index.rst b/docs/index.rst index 5b1b157..f1dbce6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -111,9 +111,21 @@ used to specify the Python version tag to use more precisely:: equates to the tag "py2.py3". --python-tag XXX Specifies the precise python version tag to use for a pure-python wheel. - -Neither of these two flags have any effect when used on a project that includes -C extension code. + --py-limited-api {cp32|cp33|cp34|...} + Specifies Python Py_LIMITED_API compatibility with + the version of CPython passed and later versions. + The wheel will be tagged cpNN.abi3.{arch} on CPython 3. + This flag does not affect Python 2 builds or alternate + Python implementations. + + To conform to the limited API, all your C + extensions must use only functions from the limited + API, pass Extension(py_limited_api=True) and e.g. + #define Py_LIMITED_API=0x03020000 depending on + the exact minimun Python you wish to support. + +The --universal and --python-tag flags have no effect when used on a +project that includes C extension code. The default for a pure Python project (if no explicit flags are given) is "pyN" where N is the major version of the Python interpreter used to build the wheel. diff --git a/wheel/bdist_wheel.py b/wheel/bdist_wheel.py index d716163..edbba05 100644 --- a/wheel/bdist_wheel.py +++ b/wheel/bdist_wheel.py @@ -12,6 +12,7 @@ import warnings import shutil import json import sys +import re try: import sysconfig @@ -40,6 +41,8 @@ from .metadata import pkginfo_to_dict from . import pep425tags, metadata from . import __version__ as wheel_version +PY_LIMITED_API_PATTERN = r'cp3\d' + def safer_name(name): return safe_name(name).replace('-', '_') @@ -77,6 +80,9 @@ class bdist_wheel(Command): ('python-tag=', None, "Python implementation compatibility tag" " (default: py%s)" % get_impl_ver()[0]), + ('py-limited-api=', None, + "Python tag (cp32|cp33|cpNN) for abi3 wheel tag" + " (default: false)"), ] boolean_options = ['keep-temp', 'skip-build', 'relative', 'universal'] @@ -98,6 +104,7 @@ class bdist_wheel(Command): self.group = None self.universal = False self.python_tag = 'py' + get_impl_ver()[0] + self.py_limited_api = False self.plat_name_supplied = False def finalize_options(self): @@ -116,6 +123,9 @@ class bdist_wheel(Command): self.root_is_pure = not (self.distribution.has_ext_modules() or self.distribution.has_c_libraries()) + if self.py_limited_api and not re.match(PY_LIMITED_API_PATTERN, self.py_limited_api): + raise ValueError("py-limited-api must match '%s'" % PY_LIMITED_API_PATTERN) + # Support legacy [wheel] section for setting universal wheel = self.distribution.get_option_dict('wheel') if 'universal' in wheel: @@ -153,13 +163,20 @@ class bdist_wheel(Command): else: impl_name = get_abbr_impl() impl_ver = get_impl_ver() - # PEP 3149 - abi_tag = str(get_abi_tag()).lower() - tag = (impl_name + impl_ver, abi_tag, plat_name) + impl = impl_name + impl_ver + # We don't work on CPython 3.1, 3.0. + if self.py_limited_api and (impl_name + impl_ver).startswith('cp3'): + impl = self.py_limited_api + abi_tag = 'abi3' + else: + abi_tag = str(get_abi_tag()).lower() + tag = (impl, abi_tag, plat_name) supported_tags = pep425tags.get_supported( supplied_platform=plat_name if self.plat_name_supplied else None) # XXX switch to this alternate implementation for non-pure: - assert tag == supported_tags[0], "%s != %s" % (tag, supported_tags[0]) + if not self.py_limited_api: + assert tag == supported_tags[0], "%s != %s" % (tag, supported_tags[0]) + assert tag in supported_tags, "would build wheel with unsupported tag %s" % tag return tag def get_archive_basename(self): diff --git a/wheel/pep425tags.py b/wheel/pep425tags.py index 5ac5d0d..49a1367 100644 --- a/wheel/pep425tags.py +++ b/wheel/pep425tags.py @@ -152,7 +152,16 @@ def get_supported(versions=None, supplied_platform=None): for abi in abis: for arch in platforms: supported.append(('%s%s' % (impl, versions[0]), abi, arch)) - + + # abi3 modules compatible with older version of Python + for version in versions[1:]: + # abi3 was introduced in Python 3.2 + if version in ('31', '30'): + break + for abi in abi3s: # empty set if not Python 3 + for arch in platforms: + supported.append(("%s%s" % (impl, version), abi, arch)) + # No abi / arch, but requires our implementation: for i, version in enumerate(versions): supported.append(('%s%s' % (impl, version), 'none', 'any')) diff --git a/wheel/test/extension.dist/extension.c b/wheel/test/extension.dist/extension.c new file mode 100644 index 0000000..a37c3fa --- /dev/null +++ b/wheel/test/extension.dist/extension.c @@ -0,0 +1,2 @@ +#define Py_LIMITED_API 0x03020000 +#include diff --git a/wheel/test/extension.dist/setup.cfg b/wheel/test/extension.dist/setup.cfg new file mode 100644 index 0000000..9f6ff39 --- /dev/null +++ b/wheel/test/extension.dist/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +py_limited_api=cp32 diff --git a/wheel/test/extension.dist/setup.py b/wheel/test/extension.dist/setup.py new file mode 100644 index 0000000..7a66845 --- /dev/null +++ b/wheel/test/extension.dist/setup.py @@ -0,0 +1,20 @@ +from setuptools import setup, Extension + +try: + unicode + def u8(s): + return s.decode('unicode-escape').encode('utf-8') +except NameError: + def u8(s): + return s.encode('utf-8') + +setup(name='extension.dist', + version='0.1', + description=u8('A testing distribution \N{SNOWMAN}'), + ext_modules=[ + Extension(name='extension', + sources=['extension.c'], + py_limited_api=True) + ], + ) + -- cgit v1.2.1