From b96985e04be87bc062129be3e7fc23ea932fd524 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Sun, 26 Jun 2016 22:47:55 -0400 Subject: refactor wheel.metadata.pkginfo_to_metadata Added a reuseable generate_requirements(extras_require) function. --- wheel/metadata.py | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/wheel/metadata.py b/wheel/metadata.py index 47b15ce..6710677 100644 --- a/wheel/metadata.py +++ b/wheel/metadata.py @@ -236,6 +236,29 @@ def convert_requirements(requirements): extras = "[%s]" % extras yield (parsed_requirement.project_name + extras + spec) +def generate_requirements(extras_require): + """ + Convert requirements from a setup()-style dictionary to ('Requires-Dist', 'requirement') + and ('Provides-Extra', 'extra') tuples. + + extras_require is a dictionary of {extra: [requirements]} as passed to setup(), + using the empty extra {'': [requirements]} to hold install_requires. + """ + for extra, depends in extras_require.items(): + condition = '' + if extra and ':' in extra: # setuptools extra:condition syntax + extra, condition = extra.split(':', 1) + extra = pkg_resources.safe_extra(extra) + if extra: + yield ('Provides-Extra', extra) + if condition: + condition += " and " + condition += "extra == '%s'" % extra + if condition: + condition = '; ' + condition + for new_req in convert_requirements(depends): + yield ('Requires-Dist', new_req + condition) + def pkginfo_to_metadata(egg_info_path, pkginfo_path): """ Convert .egg-info directory with PKG-INFO to the Metadata 1.3 aka @@ -249,18 +272,8 @@ def pkginfo_to_metadata(egg_info_path, pkginfo_path): requires = requires_file.read() for extra, reqs in sorted(pkg_resources.split_sections(requires), key=lambda x: x[0] or ''): - condition = '' - if extra and ':' in extra: # setuptools extra:condition syntax - extra, condition = extra.split(':', 1) - if extra: - pkg_info['Provides-Extra'] = extra - if condition: - condition += " and " - condition += 'extra == %s' % repr(extra) - if condition: - condition = '; ' + condition - for new_req in sorted(convert_requirements(reqs)): - pkg_info['Requires-Dist'] = new_req + condition + for item in generate_requirements({extra: reqs}): + pkg_info[item[0]] = item[1] description = pkg_info['Description'] if description: -- cgit v1.2.1 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 From 0fac6fbefc25e3dfb8f6e49a06629f059258780d Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Wed, 14 Sep 2016 18:15:10 -0400 Subject: use dict-format entry_points --- setup.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index fc27b85..bd1c900 100644 --- a/setup.py +++ b/setup.py @@ -48,11 +48,13 @@ setup(name='wheel', tests_require=['jsonschema', 'pytest', 'coverage', 'pytest-cov'], include_package_data=True, zip_safe=False, - entry_points = """\ -[console_scripts] -wheel = wheel.tool:main - -[distutils.commands] -bdist_wheel = wheel.bdist_wheel:bdist_wheel""" + entry_points = { + 'console_scripts': [ + 'wheel=wheel.tool:main' + ], + 'distutils.commands': [ + 'bdist_wheel=wheel.bdist_wheel:bdist_wheel' + ] + } ) -- cgit v1.2.1 From f478e2b5efd3bc5707f72f9334ec4f1c881ff6b2 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Wed, 14 Sep 2016 18:16:16 -0400 Subject: prerelease --- wheel/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wheel/__init__.py b/wheel/__init__.py index 1dbfedc..7b3d278 100644 --- a/wheel/__init__.py +++ b/wheel/__init__.py @@ -1,2 +1,2 @@ # __variables__ with double-quoted values will be available in setup.py: -__version__ = "0.30.0.dev0" +__version__ = "0.30.0.a0" -- cgit v1.2.1 From 235e161cfec6d80d2f8ca92fb52c11b4100eaba0 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Wed, 14 Sep 2016 18:44:41 -0400 Subject: update CHANGES --- CHANGES.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index f53b572..5e40718 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,12 @@ +0.30.0 +====== +- Add py-limited-api {cp32|cp33|cp34|...} flag to produce cpNN.abi3.{arch} + tags on CPython 3. +- Improve Python, abi tagging for `wheel convert`. Thanks Ales Erjavec. +- Much improved use of context managers for file handling. Thanks Kyle + Stewart. +- Convert absolute imports to relative. Thanks Ashish Bhate. + 0.29.0 ====== - Fix compression type of files in archive (Issue #155, Pull Request #62, -- cgit v1.2.1 From 5076a511f1444b3520ca37ed22aa692f6fd49305 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Fri, 7 Jul 2017 17:03:28 -0700 Subject: Document support for Python 3.6 and add it to the testing matrix Drops support for Python 2.6. Remove now unnecessary import workarounds. Add missing trove classifiers. --- CHANGES.txt | 1 + METADATA.in | 8 ++++++-- setup.py | 29 +++++++++++++++-------------- tox.ini | 2 +- wheel/bdist_wheel.py | 6 ------ wheel/metadata.py | 5 +---- wheel/pep425tags.py | 6 +----- wheel/util.py | 5 +---- 8 files changed, 26 insertions(+), 36 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5e40718..dfbb12b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,7 @@ - Much improved use of context managers for file handling. Thanks Kyle Stewart. - Convert absolute imports to relative. Thanks Ashish Bhate. +- Remove support for Python 2.6. 0.29.0 ====== diff --git a/METADATA.in b/METADATA.in index 2827551..18ebf32 100644 --- a/METADATA.in +++ b/METADATA.in @@ -8,16 +8,20 @@ Author-email: dholth@fastmail.fm License: MIT Keywords: wheel,packaging Platform: UNKNOWN -Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy Provides-Extra: tool Provides-Extra: signatures Requires-Dist: keyring; extra == 'signatures' diff --git a/setup.py b/setup.py index bd1c900..cde619a 100644 --- a/setup.py +++ b/setup.py @@ -15,17 +15,21 @@ setup(name='wheel', description='A built-package format for Python.', long_description=README + '\n\n' + CHANGES, classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.2", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - ], + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.2", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + ], author='Daniel Holth', author_email='dholth@fastmail.fm', url='https://bitbucket.org/pypa/wheel/', @@ -38,10 +42,8 @@ setup(name='wheel', 'wheel.signatures' ], extras_require={ - ':python_version=="2.6"': ['argparse'], 'signatures': ['keyring', 'keyrings.alt'], 'signatures:sys_platform!="win32"': ['pyxdg'], - 'signatures:python_version=="2.6"': ['importlib'], 'faster-signatures': ['ed25519ll'], 'tool': [] }, @@ -57,4 +59,3 @@ setup(name='wheel', ] } ) - diff --git a/tox.ini b/tox.ini index 9590ab4..309d58d 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py26, py27, pypy, py33, py34, py35 +envlist = py27, pypy, py33, py34, py35, py36 [testenv] commands = diff --git a/wheel/bdist_wheel.py b/wheel/bdist_wheel.py index edbba05..dfcc910 100644 --- a/wheel/bdist_wheel.py +++ b/wheel/bdist_wheel.py @@ -14,12 +14,6 @@ import json import sys import re -try: - import sysconfig -except ImportError: # pragma nocover - # Python < 2.7 - import distutils.sysconfig as sysconfig - import pkg_resources safe_name = pkg_resources.safe_name diff --git a/wheel/metadata.py b/wheel/metadata.py index 341c4b0..2b123f9 100644 --- a/wheel/metadata.py +++ b/wheel/metadata.py @@ -5,10 +5,7 @@ Tools for converting old- to new-style metadata. from collections import namedtuple from .pkginfo import read_pkg_info from .util import OrderedDefaultDict -try: - from collections import OrderedDict -except ImportError: - OrderedDict = dict +from collections import OrderedDict import re import os.path diff --git a/wheel/pep425tags.py b/wheel/pep425tags.py index 49a1367..a7bd4a9 100644 --- a/wheel/pep425tags.py +++ b/wheel/pep425tags.py @@ -3,11 +3,7 @@ import sys import warnings -try: - import sysconfig -except ImportError: # pragma nocover - # Python < 2.7 - import distutils.sysconfig as sysconfig +import sysconfig import distutils.util diff --git a/wheel/util.py b/wheel/util.py index 5268813..20f386f 100644 --- a/wheel/util.py +++ b/wheel/util.py @@ -5,10 +5,7 @@ import os import base64 import json import hashlib -try: - from collections import OrderedDict -except ImportError: - OrderedDict = dict +from collections import OrderedDict __all__ = ['urlsafe_b64encode', 'urlsafe_b64decode', 'utf8', 'to_json', 'from_json', 'matches_requirement'] -- cgit v1.2.1