11"""Generate and work with PEP 425 Compatibility Tags."""
22from __future__ import absolute_import
33
4- import distutils .util
54import logging
6- import platform
75import re
8- import sys
9- import sysconfig
106
117from pip ._vendor .packaging .tags import (
128 Tag ,
9+ compatible_tags ,
10+ cpython_tags ,
11+ generic_tags ,
1312 interpreter_name ,
1413 interpreter_version ,
1514 mac_platforms ,
1615)
17- from pip ._vendor .six import PY2
1816
19- import pip ._internal .utils .glibc
2017from pip ._internal .utils .typing import MYPY_CHECK_RUNNING
2118
2219if MYPY_CHECK_RUNNING :
23- from typing import (
24- Callable , List , Optional , Tuple , Union
25- )
20+ from typing import List , Optional , Tuple
21+
22+ from pip . _vendor . packaging . tags import PythonVersion
2623
2724logger = logging .getLogger (__name__ )
2825
2926_osx_arch_pat = re .compile (r'(.+)_(\d+)_(\d+)_(.+)' )
3027
3128
32- def get_config_var (var ):
33- # type: (str) -> Optional[str]
34- return sysconfig .get_config_var (var )
35-
36-
3729def version_info_to_nodot (version_info ):
3830 # type: (Tuple[int, ...]) -> str
3931 # Only use up to the first two numbers.
4032 return '' .join (map (str , version_info [:2 ]))
4133
4234
43- def get_impl_version_info ():
44- # type: () -> Tuple[int, ...]
45- """Return sys.version_info-like tuple for use in decrementing the minor
46- version."""
47- if interpreter_name () == 'pp' :
48- # as per https://github.com/pypa/pip/issues/2882
49- # attrs exist only on pypy
50- return (sys .version_info [0 ],
51- sys .pypy_version_info .major , # type: ignore
52- sys .pypy_version_info .minor ) # type: ignore
53- else :
54- return sys .version_info [0 ], sys .version_info [1 ]
55-
56-
57- def get_flag (var , fallback , expected = True , warn = True ):
58- # type: (str, Callable[..., bool], Union[bool, int], bool) -> bool
59- """Use a fallback method for determining SOABI flags if the needed config
60- var is unset or unavailable."""
61- val = get_config_var (var )
62- if val is None :
63- if warn :
64- logger .debug ("Config variable '%s' is unset, Python ABI tag may "
65- "be incorrect" , var )
66- return fallback ()
67- return val == expected
68-
69-
70- def get_abi_tag ():
71- # type: () -> Optional[str]
72- """Return the ABI tag based on SOABI (if available) or emulate SOABI
73- (CPython 2, PyPy)."""
74- soabi = get_config_var ('SOABI' )
75- impl = interpreter_name ()
76- abi = None # type: Optional[str]
77-
78- if not soabi and impl in {'cp' , 'pp' } and hasattr (sys , 'maxunicode' ):
79- d = ''
80- m = ''
81- u = ''
82- is_cpython = (impl == 'cp' )
83- if get_flag (
84- 'Py_DEBUG' , lambda : hasattr (sys , 'gettotalrefcount' ),
85- warn = is_cpython ):
86- d = 'd'
87- if sys .version_info < (3 , 8 ) and get_flag (
88- 'WITH_PYMALLOC' , lambda : is_cpython , warn = is_cpython ):
89- m = 'm'
90- if sys .version_info < (3 , 3 ) and get_flag (
91- 'Py_UNICODE_SIZE' , lambda : sys .maxunicode == 0x10ffff ,
92- expected = 4 , warn = is_cpython ):
93- u = 'u'
94- abi = '%s%s%s%s%s' % (impl , interpreter_version (), d , m , u )
95- elif soabi and soabi .startswith ('cpython-' ):
96- abi = 'cp' + soabi .split ('-' )[1 ]
97- elif soabi :
98- abi = soabi .replace ('.' , '_' ).replace ('-' , '_' )
99-
100- return abi
101-
102-
103- def _is_running_32bit ():
104- # type: () -> bool
105- return sys .maxsize == 2147483647
106-
107-
108- def get_platform ():
109- # type: () -> str
110- """Return our platform name 'win32', 'linux_x86_64'"""
111- if sys .platform == 'darwin' :
112- # distutils.util.get_platform() returns the release based on the value
113- # of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may
114- # be significantly older than the user's current machine.
115- release , _ , machine = platform .mac_ver ()
116- split_ver = release .split ('.' )
117-
118- if machine == "x86_64" and _is_running_32bit ():
119- machine = "i386"
120- elif machine == "ppc64" and _is_running_32bit ():
121- machine = "ppc"
122-
123- return 'macosx_{}_{}_{}' .format (split_ver [0 ], split_ver [1 ], machine )
124-
125- # XXX remove distutils dependency
126- result = distutils .util .get_platform ().replace ('.' , '_' ).replace ('-' , '_' )
127- if result == "linux_x86_64" and _is_running_32bit ():
128- # 32 bit Python program (running on a 64 bit Linux): pip should only
129- # install and run 32 bit compiled extensions in that case.
130- result = "linux_i686"
131-
132- return result
133-
134-
135- def is_linux_armhf ():
136- # type: () -> bool
137- if get_platform () != "linux_armv7l" :
138- return False
139- # hard-float ABI can be detected from the ELF header of the running
140- # process
141- try :
142- with open (sys .executable , 'rb' ) as f :
143- elf_header_raw = f .read (40 ) # read 40 first bytes of ELF header
144- except (IOError , OSError , TypeError ):
145- return False
146- if elf_header_raw is None or len (elf_header_raw ) < 40 :
147- return False
148- if isinstance (elf_header_raw , str ):
149- elf_header = [ord (c ) for c in elf_header_raw ]
150- else :
151- elf_header = [b for b in elf_header_raw ]
152- result = elf_header [0 :4 ] == [0x7f , 0x45 , 0x4c , 0x46 ] # ELF magic number
153- result &= elf_header [4 :5 ] == [1 ] # 32-bit ELF
154- result &= elf_header [5 :6 ] == [1 ] # little-endian
155- result &= elf_header [18 :20 ] == [0x28 , 0 ] # ARM machine
156- result &= elf_header [39 :40 ] == [5 ] # ARM EABIv5
157- result &= (elf_header [37 :38 ][0 ] & 4 ) == 4 # EF_ARM_ABI_FLOAT_HARD
158- return result
159-
160-
161- def is_manylinux1_compatible ():
162- # type: () -> bool
163- # Only Linux, and only x86-64 / i686
164- if get_platform () not in {"linux_x86_64" , "linux_i686" }:
165- return False
166-
167- # Check for presence of _manylinux module
168- try :
169- import _manylinux
170- return bool (_manylinux .manylinux1_compatible )
171- except (ImportError , AttributeError ):
172- # Fall through to heuristic check below
173- pass
174-
175- # Check glibc version. CentOS 5 uses glibc 2.5.
176- return pip ._internal .utils .glibc .have_compatible_glibc (2 , 5 )
177-
178-
179- def is_manylinux2010_compatible ():
180- # type: () -> bool
181- # Only Linux, and only x86-64 / i686
182- if get_platform () not in {"linux_x86_64" , "linux_i686" }:
183- return False
184-
185- # Check for presence of _manylinux module
186- try :
187- import _manylinux
188- return bool (_manylinux .manylinux2010_compatible )
189- except (ImportError , AttributeError ):
190- # Fall through to heuristic check below
191- pass
192-
193- # Check glibc version. CentOS 6 uses glibc 2.12.
194- return pip ._internal .utils .glibc .have_compatible_glibc (2 , 12 )
195-
196-
197- def is_manylinux2014_compatible ():
198- # type: () -> bool
199- # Only Linux, and only supported architectures
200- platform = get_platform ()
201- if platform not in {"linux_x86_64" , "linux_i686" , "linux_aarch64" ,
202- "linux_armv7l" , "linux_ppc64" , "linux_ppc64le" ,
203- "linux_s390x" }:
204- return False
205-
206- # check for hard-float ABI in case we're running linux_armv7l not to
207- # install hard-float ABI wheel in a soft-float ABI environment
208- if platform == "linux_armv7l" and not is_linux_armhf ():
209- return False
210-
211- # Check for presence of _manylinux module
212- try :
213- import _manylinux
214- return bool (_manylinux .manylinux2014_compatible )
215- except (ImportError , AttributeError ):
216- # Fall through to heuristic check below
217- pass
218-
219- # Check glibc version. CentOS 7 uses glibc 2.17.
220- return pip ._internal .utils .glibc .have_compatible_glibc (2 , 17 )
221-
222-
223- def get_all_minor_versions_as_strings (version_info ):
224- # type: (Tuple[int, ...]) -> List[str]
225- versions = []
226- major = version_info [:- 1 ]
227- # Support all previous minor Python versions.
228- for minor in range (version_info [- 1 ], - 1 , - 1 ):
229- versions .append ('' .join (map (str , major + (minor ,))))
230- return versions
231-
232-
23335def _mac_platforms (arch ):
23436 # type: (str) -> List[str]
23537 match = _osx_arch_pat .match (arch )
@@ -273,27 +75,35 @@ def _custom_manylinux_platforms(arch):
27375 return arches
27476
27577
276- def _get_custom_platforms (arch , platform ):
277- # type: (str, Optional[str] ) -> List[str]
78+ def _get_custom_platforms (arch ):
79+ # type: (str) -> List[str]
27880 arch_prefix , arch_sep , arch_suffix = arch .partition ('_' )
27981 if arch .startswith ('macosx' ):
28082 arches = _mac_platforms (arch )
28183 elif arch_prefix in ['manylinux2014' , 'manylinux2010' ]:
28284 arches = _custom_manylinux_platforms (arch )
283- elif platform is None :
284- arches = []
285- if is_manylinux2014_compatible ():
286- arches .append ('manylinux2014' + arch_sep + arch_suffix )
287- if is_manylinux2010_compatible ():
288- arches .append ('manylinux2010' + arch_sep + arch_suffix )
289- if is_manylinux1_compatible ():
290- arches .append ('manylinux1' + arch_sep + arch_suffix )
291- arches .append (arch )
29285 else :
29386 arches = [arch ]
29487 return arches
29588
29689
90+ def _get_python_version (version ):
91+ # type: (str) -> PythonVersion
92+ if len (version ) > 1 :
93+ return int (version [0 ]), int (version [1 :])
94+ else :
95+ return (int (version [0 ]),)
96+
97+
98+ def _get_custom_interpreter (implementation = None , version = None ):
99+ # type: (Optional[str], Optional[str]) -> str
100+ if implementation is None :
101+ implementation = interpreter_name ()
102+ if version is None :
103+ version = interpreter_version ()
104+ return "{}{}" .format (implementation , version )
105+
106+
297107def get_supported (
298108 version = None , # type: Optional[str]
299109 platform = None , # type: Optional[str]
@@ -313,59 +123,45 @@ def get_supported(
313123 :param abi: specify the exact abi you want valid
314124 tags for, or None. If None, use the local interpreter abi.
315125 """
316- supported = []
317-
318- # Versions must be given with respect to the preference
319- if version is None :
320- version_info = get_impl_version_info ()
321- versions = get_all_minor_versions_as_strings (version_info )
126+ supported = [] # type: List[Tag]
127+
128+ python_version = None # type: Optional[PythonVersion]
129+ if version is not None :
130+ python_version = _get_python_version (version )
131+
132+ interpreter = _get_custom_interpreter (impl , version )
133+
134+ abis = None # type: Optional[List[str]]
135+ if abi is not None :
136+ abis = [abi ]
137+
138+ platforms = None # type: Optional[List[str]]
139+ if platform is not None :
140+ platforms = _get_custom_platforms (platform )
141+
142+ is_cpython = (impl or interpreter_name ()) == "cp"
143+ if is_cpython :
144+ supported .extend (
145+ cpython_tags (
146+ python_version = python_version ,
147+ abis = abis ,
148+ platforms = platforms ,
149+ )
150+ )
322151 else :
323- versions = [version ]
324- current_version = versions [0 ]
325- other_versions = versions [1 :]
326-
327- impl = impl or interpreter_name ()
328-
329- abis = [] # type: List[str]
330-
331- abi = abi or get_abi_tag ()
332- if abi :
333- abis [0 :0 ] = [abi ]
334-
335- supports_abi3 = not PY2 and impl == "cp"
336-
337- if supports_abi3 :
338- abis .append ("abi3" )
339-
340- abis .append ('none' )
341-
342- arches = _get_custom_platforms (platform or get_platform (), platform )
343-
344- # Current version, current API (built specifically for our Python):
345- for abi in abis :
346- for arch in arches :
347- supported .append (('%s%s' % (impl , current_version ), abi , arch ))
348-
349- # abi3 modules compatible with older version of Python
350- if supports_abi3 :
351- for version in other_versions :
352- # abi3 was introduced in Python 3.2
353- if version in {'31' , '30' }:
354- break
355- for arch in arches :
356- supported .append (("%s%s" % (impl , version ), "abi3" , arch ))
357-
358- # Has binaries, does not use the Python API:
359- for arch in arches :
360- supported .append (('py%s' % (current_version [0 ]), 'none' , arch ))
361-
362- # No abi / arch, but requires our implementation:
363- supported .append (('%s%s' % (impl , current_version ), 'none' , 'any' ))
364-
365- # No abi / arch, generic Python
366- supported .append (('py%s' % (current_version ,), 'none' , 'any' ))
367- supported .append (('py%s' % (current_version [0 ]), 'none' , 'any' ))
368- for version in other_versions :
369- supported .append (('py%s' % (version ,), 'none' , 'any' ))
152+ supported .extend (
153+ generic_tags (
154+ interpreter = interpreter ,
155+ abis = abis ,
156+ platforms = platforms ,
157+ )
158+ )
159+ supported .extend (
160+ compatible_tags (
161+ python_version = python_version ,
162+ interpreter = interpreter ,
163+ platforms = platforms ,
164+ )
165+ )
370166
371- return [ Tag ( * parts ) for parts in supported ]
167+ return supported
0 commit comments