Merge pull request #3075 from natefoo/py2-soabi

SOABI support for Python 2.X and PyPy
This commit is contained in:
Donald Stufft 2015-10-14 17:59:01 -04:00
commit 461b7aaad3
2 changed files with 149 additions and 19 deletions

View File

@ -15,6 +15,14 @@ import distutils.util
_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)')
def get_config_var(var):
try:
return sysconfig.get_config_var(var)
except IOError as e: # Issue #1074
warnings.warn("{0}".format(e), RuntimeWarning)
return None
def get_abbr_impl():
"""Return abbreviated implementation name."""
if hasattr(sys, 'pypy_version_info'):
@ -30,7 +38,67 @@ def get_abbr_impl():
def get_impl_ver():
"""Return implementation version."""
return ''.join(map(str, sys.version_info[:2]))
impl_ver = get_config_var("py_version_nodot")
if not impl_ver or get_abbr_impl() == 'pp':
impl_ver = ''.join(map(str, get_impl_version_info()))
return impl_ver
def get_impl_version_info():
"""Return sys.version_info-like tuple for use in decrementing the minor
version."""
if get_abbr_impl() == 'pp':
# as per https://github.com/pypa/pip/issues/2882
return (sys.version_info[0], sys.pypy_version_info.major,
sys.pypy_version_info.minor)
else:
return sys.version_info[0], sys.version_info[1]
def get_flag(var, fallback, expected=True, warn=True):
"""Use a fallback method for determining SOABI flags if the needed config
var is unset or unavailable."""
val = get_config_var(var)
if val is None:
if warn:
warnings.warn("Config variable '{0}' is unset, Python ABI tag may "
"be incorrect".format(var), RuntimeWarning, 2)
return fallback()
return val == expected
def get_abi_tag():
"""Return the ABI tag based on SOABI (if available) or emulate SOABI
(CPython 2, PyPy)."""
soabi = get_config_var('SOABI')
impl = get_abbr_impl()
if not soabi and impl in ('cp', 'pp') and hasattr(sys, 'maxunicode'):
d = ''
m = ''
u = ''
if get_flag('Py_DEBUG',
lambda: hasattr(sys, 'gettotalrefcount'),
warn=(impl == 'cp')):
d = 'd'
if get_flag('WITH_PYMALLOC',
lambda: impl == 'cp',
warn=(impl == 'cp')):
m = 'm'
if get_flag('Py_UNICODE_SIZE',
lambda: sys.maxunicode == 0x10ffff,
expected=4,
warn=(impl == 'cp' and
sys.version_info < (3, 3))) \
and sys.version_info < (3, 3):
u = 'u'
abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u)
elif soabi and soabi.startswith('cpython-'):
abi = 'cp' + soabi.split('-')[1]
elif soabi:
abi = soabi.replace('.', '_').replace('-', '_')
else:
abi = None
return abi
def get_platform():
@ -51,23 +119,19 @@ def get_supported(versions=None, noarch=False):
# Versions must be given with respect to the preference
if versions is None:
versions = []
major = sys.version_info[0]
version_info = get_impl_version_info()
major = version_info[:-1]
# Support all previous minor Python versions.
for minor in range(sys.version_info[1], -1, -1):
versions.append(''.join(map(str, (major, minor))))
for minor in range(version_info[-1], -1, -1):
versions.append(''.join(map(str, major + (minor,))))
impl = get_abbr_impl()
abis = []
try:
soabi = sysconfig.get_config_var('SOABI')
except IOError as e: # Issue #1074
warnings.warn("{0}".format(e), RuntimeWarning)
soabi = None
if soabi and soabi.startswith('cpython-'):
abis[0:0] = ['cp' + soabi.split('-')[1]]
abi = get_abi_tag()
if abi:
abis[0:0] = [abi]
abi3s = set()
import imp

View File

@ -1,5 +1,6 @@
"""Tests for wheel binary packages and .dist-info."""
import os
import sys
import pytest
from mock import patch, Mock
@ -294,6 +295,52 @@ class TestWheelFile(object):
class TestPEP425Tags(object):
def mock_get_config_var(self, **kwd):
"""
Patch sysconfig.get_config_var for arbitrary keys.
"""
import pip.pep425tags
get_config_var = pip.pep425tags.sysconfig.get_config_var
def _mock_get_config_var(var):
if var in kwd:
return kwd[var]
return get_config_var(var)
return _mock_get_config_var
def abi_tag_unicode(self, flags, config_vars):
"""
Used to test ABI tags, verify correct use of the `u` flag
"""
import pip.pep425tags
config_vars.update({'SOABI': None})
base = pip.pep425tags.get_abbr_impl() + pip.pep425tags.get_impl_ver()
if sys.version_info < (3, 3):
config_vars.update({'Py_UNICODE_SIZE': 2})
mock_gcf = self.mock_get_config_var(**config_vars)
with patch('pip.pep425tags.sysconfig.get_config_var', mock_gcf):
abi_tag = pip.pep425tags.get_abi_tag()
assert abi_tag == base + flags
config_vars.update({'Py_UNICODE_SIZE': 4})
mock_gcf = self.mock_get_config_var(**config_vars)
with patch('pip.pep425tags.sysconfig.get_config_var', mock_gcf):
abi_tag = pip.pep425tags.get_abi_tag()
assert abi_tag == base + flags + 'u'
else:
# On Python >= 3.3, UCS-4 is essentially permanently enabled, and
# Py_UNICODE_SIZE is None. SOABI on these builds does not include
# the 'u' so manual SOABI detection should not do so either.
config_vars.update({'Py_UNICODE_SIZE': None})
mock_gcf = self.mock_get_config_var(**config_vars)
with patch('pip.pep425tags.sysconfig.get_config_var', mock_gcf):
abi_tag = pip.pep425tags.get_abi_tag()
assert abi_tag == base + flags
def test_broken_sysconfig(self):
"""
Test that pep425tags still works when sysconfig is broken.
@ -314,14 +361,9 @@ class TestPEP425Tags(object):
"""
import pip.pep425tags
get_config_var = pip.pep425tags.sysconfig.get_config_var
mock_gcf = self.mock_get_config_var(SOABI='cpython-35m-darwin')
def mock_soabi(var):
if var == 'SOABI':
return 'cpython-35m-darwin'
return get_config_var(var)
with patch('pip.pep425tags.sysconfig.get_config_var', mock_soabi):
with patch('pip.pep425tags.sysconfig.get_config_var', mock_gcf):
supported = pip.pep425tags.get_supported()
for (py, abi, plat) in supported:
@ -329,6 +371,30 @@ class TestPEP425Tags(object):
assert '-' not in abi
assert '-' not in plat
def test_manual_abi_noflags(self):
"""
Test that no flags are set on a non-PyDebug, non-Pymalloc ABI tag.
"""
self.abi_tag_unicode('', {'Py_DEBUG': False, 'WITH_PYMALLOC': False})
def test_manual_abi_d_flag(self):
"""
Test that the `d` flag is set on a PyDebug, non-Pymalloc ABI tag.
"""
self.abi_tag_unicode('d', {'Py_DEBUG': True, 'WITH_PYMALLOC': False})
def test_manual_abi_m_flag(self):
"""
Test that the `m` flag is set on a non-PyDebug, Pymalloc ABI tag.
"""
self.abi_tag_unicode('m', {'Py_DEBUG': False, 'WITH_PYMALLOC': True})
def test_manual_abi_dm_flags(self):
"""
Test that the `dm` flags are set on a PyDebug, Pymalloc ABI tag.
"""
self.abi_tag_unicode('dm', {'Py_DEBUG': True, 'WITH_PYMALLOC': True})
class TestMoveWheelFiles(object):
"""