Commits

Paul Moore  committed 88ecc79 Merge

Merged in pmoore/wheel (pull request #42)

Add a --python-tag argument for bdist_wheel

  • Participants
  • Parent commits 75e6f99, 695f7ae

Comments (0)

Files changed (4)

File docs/index.rst

 install-scripts packagename` calls setuptools to write the appropriate
 scripts wrappers after an install.
 
+Defining the Python version
+---------------------------
+
+The `bdist_wheel` command automatically determines the correct tags to use for
+the generated wheel.  These are based on the Python interpreter used to
+generate the wheel and whether the project contains C extension code or not.
+While this is usually correct for C code, it can be too conservative for pure
+Python code.  The bdist_wheel command therefore supports two flags that can be
+used to specify the Python version tag to use more precisely::
+
+    --universal        Specifies that a pure-python wheel is "universal"
+                       (i.e., it works on any version of Python).  This
+                       equates to the tag "py2.py3".
+    --python-tag XXX   Specifies the precide 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.
+
+A reasonable use of the `--python-tag` argument would be for a project that
+uses Python syntax only introduced in a particular Python version.  There are
+no current examples of this, but if wheels had been available when Python 2.5
+was released (the first version containing the `with` statement), wheels for a
+project that used the `with` statement would typically use `--python-tag py25`.
+
+Typically, projects would not specify python tags on the command line, but
+would use `setup.cfg` to set them as a project default::
+
+    [bdist_wheel]
+    universal=1
+
+or::
+
+    [bdist_wheel]
+    python-tag = py32
+
+
 Automatically sign wheel files
 ------------------------------
 
     ed25519ll; extra == 'faster-signatures'
 license-file = LICENSE.txt
 
-[wheel]
+[bdist_wheel]
 # use py2.py3 tag for pure-python dist:
 universal=1

File wheel/bdist_wheel.py

                     ('universal', None,
                      "make a universal wheel"
                      " (default: false)"),
+                    ('python-tag=', None,
+                     "Python implementation compatibility tag"
+                     " (default: py%s)" % get_impl_ver()[0]),
                     ]
 
     boolean_options = ['keep-temp', 'skip-build', 'relative', 'universal']
         self.group = None
         self.skip_scripts = False
         self.universal = False
+        self.python_tag = 'py' + get_impl_ver()[0]
 
     def finalize_options(self):
         if self.bdist_dir is None:
     def get_tag(self):
         supported_tags = pep425tags.get_supported()
 
-        purity = self.distribution.is_pure()
-        impl_ver = get_impl_ver()
-        abi_tag = 'none'
-        plat_name = 'any'
-        impl_name = 'py'
-        if purity:
+        if self.distribution.is_pure():
             if self.universal:
-                impl_name = 'py2.py3'
-                impl_ver = ''
-            tag = (impl_name + impl_ver, abi_tag, plat_name)
+                impl = 'py2.py3'
+            else:
+                impl = self.python_tag
+            tag = (impl, 'none', 'any')
         else:
             plat_name = self.plat_name
             if plat_name is None:
                 plat_name = get_platform()
             plat_name = plat_name.replace('-', '_').replace('.', '_')
             impl_name = get_abbr_impl()
+            impl_ver = get_impl_ver()
             # PEP 3149 -- no SOABI in Py 2
             # For PyPy?
             # "pp%s%s" % (sys.pypy_version_info.major,
             # sys.pypy_version_info.minor)
-            abi_tag = sysconfig.get_config_vars().get('SOABI', abi_tag)
+            abi_tag = sysconfig.get_config_vars().get('SOABI', 'none')
             if abi_tag.startswith('cpython-'):
                 abi_tag = 'cp' + abi_tag.rsplit('-', 1)[-1]
 

File wheel/test/test_tagopt.py

+"""
+Tests for the bdist_wheel tag options (--python-tag and --universal)
+"""
+
+import os
+import sys
+import pytest
+import py.path
+import tempfile
+import subprocess
+
+SETUP_PY = """\
+from setuptools import setup
+
+setup(
+    name="Test",
+    version="1.0",
+    author_email="author@example.com",
+    py_modules=["test"],
+)
+"""
+
+@pytest.fixture
+def temp_pkg(request):
+    tempdir = tempfile.TemporaryDirectory()
+    def fin():
+        tempdir.cleanup()
+    request.addfinalizer(fin)
+    temppath = py.path.local(tempdir.name)
+    temppath.join('test.py').write('print("Hello, world")')
+    temppath.join('setup.py').write(SETUP_PY)
+    return temppath
+
+def test_default_tag(temp_pkg):
+    subprocess.check_call([sys.executable, 'setup.py', 'bdist_wheel'],
+            cwd=str(temp_pkg))
+    dist_dir = temp_pkg.join('dist')
+    assert dist_dir.check(dir=1)
+    wheels = dist_dir.listdir()
+    assert len(wheels) == 1
+    assert wheels[0].basename.startswith('Test-1.0-py%s-' % (sys.version[0],))
+    assert wheels[0].ext == '.whl'
+
+def test_explicit_tag(temp_pkg):
+    subprocess.check_call(
+        [sys.executable, 'setup.py', 'bdist_wheel', '--python-tag=py32'],
+        cwd=str(temp_pkg))
+    dist_dir = temp_pkg.join('dist')
+    assert dist_dir.check(dir=1)
+    wheels = dist_dir.listdir()
+    assert len(wheels) == 1
+    assert wheels[0].basename.startswith('Test-1.0-py32-')
+    assert wheels[0].ext == '.whl'
+
+def test_universal_tag(temp_pkg):
+    subprocess.check_call(
+        [sys.executable, 'setup.py', 'bdist_wheel', '--universal'],
+        cwd=str(temp_pkg))
+    dist_dir = temp_pkg.join('dist')
+    assert dist_dir.check(dir=1)
+    wheels = dist_dir.listdir()
+    assert len(wheels) == 1
+    assert wheels[0].basename.startswith('Test-1.0-py2.py3-')
+    assert wheels[0].ext == '.whl'
+
+def test_universal_beats_explicit_tag(temp_pkg):
+    subprocess.check_call(
+        [sys.executable, 'setup.py', 'bdist_wheel', '--universal', '--python-tag=py32'],
+        cwd=str(temp_pkg))
+    dist_dir = temp_pkg.join('dist')
+    assert dist_dir.check(dir=1)
+    wheels = dist_dir.listdir()
+    assert len(wheels) == 1
+    assert wheels[0].basename.startswith('Test-1.0-py2.py3-')
+    assert wheels[0].ext == '.whl'
+
+def test_universal_in_setup_cfg(temp_pkg):
+    temp_pkg.join('setup.cfg').write('[bdist_wheel]\nuniversal=1')
+    subprocess.check_call(
+        [sys.executable, 'setup.py', 'bdist_wheel'],
+        cwd=str(temp_pkg))
+    dist_dir = temp_pkg.join('dist')
+    assert dist_dir.check(dir=1)
+    wheels = dist_dir.listdir()
+    assert len(wheels) == 1
+    assert wheels[0].basename.startswith('Test-1.0-py2.py3-')
+    assert wheels[0].ext == '.whl'
+
+def test_pythontag_in_setup_cfg(temp_pkg):
+    temp_pkg.join('setup.cfg').write('[bdist_wheel]\npython_tag=py32')
+    subprocess.check_call(
+        [sys.executable, 'setup.py', 'bdist_wheel'],
+        cwd=str(temp_pkg))
+    dist_dir = temp_pkg.join('dist')
+    assert dist_dir.check(dir=1)
+    wheels = dist_dir.listdir()
+    assert len(wheels) == 1
+    assert wheels[0].basename.startswith('Test-1.0-py32-')
+    assert wheels[0].ext == '.whl'
+
+def test_legacy_wheel_section_in_setup_cfg(temp_pkg):
+    temp_pkg.join('setup.cfg').write('[wheel]\nuniversal=1')
+    subprocess.check_call(
+        [sys.executable, 'setup.py', 'bdist_wheel'],
+        cwd=str(temp_pkg))
+    dist_dir = temp_pkg.join('dist')
+    assert dist_dir.check(dir=1)
+    wheels = dist_dir.listdir()
+    assert len(wheels) == 1
+    assert wheels[0].basename.startswith('Test-1.0-py2.py3-')
+    assert wheels[0].ext == '.whl'
+