1
1
"""Generate and work with PEP 425 Compatibility Tags."""
2
2
from __future__ import absolute_import
3
3
4
- import distutils .util
5
4
import logging
6
- import platform
7
5
import re
8
- import sys
9
- import sysconfig
10
6
11
7
from pip ._vendor .packaging .tags import (
12
8
Tag ,
9
+ compatible_tags ,
10
+ cpython_tags ,
11
+ generic_tags ,
13
12
interpreter_name ,
14
13
interpreter_version ,
15
14
mac_platforms ,
16
15
)
17
- from pip ._vendor .six import PY2
18
16
19
- import pip ._internal .utils .glibc
20
17
from pip ._internal .utils .typing import MYPY_CHECK_RUNNING
21
18
22
19
if 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
26
23
27
24
logger = logging .getLogger (__name__ )
28
25
29
26
_osx_arch_pat = re .compile (r'(.+)_(\d+)_(\d+)_(.+)' )
30
27
31
28
32
- def get_config_var (var ):
33
- # type: (str) -> Optional[str]
34
- return sysconfig .get_config_var (var )
35
-
36
-
37
29
def version_info_to_nodot (version_info ):
38
30
# type: (Tuple[int, ...]) -> str
39
31
# Only use up to the first two numbers.
40
32
return '' .join (map (str , version_info [:2 ]))
41
33
42
34
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
-
233
35
def _mac_platforms (arch ):
234
36
# type: (str) -> List[str]
235
37
match = _osx_arch_pat .match (arch )
@@ -273,27 +75,35 @@ def _custom_manylinux_platforms(arch):
273
75
return arches
274
76
275
77
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]
278
80
arch_prefix , arch_sep , arch_suffix = arch .partition ('_' )
279
81
if arch .startswith ('macosx' ):
280
82
arches = _mac_platforms (arch )
281
83
elif arch_prefix in ['manylinux2014' , 'manylinux2010' ]:
282
84
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 )
292
85
else :
293
86
arches = [arch ]
294
87
return arches
295
88
296
89
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
+
297
107
def get_supported (
298
108
version = None , # type: Optional[str]
299
109
platform = None , # type: Optional[str]
@@ -313,59 +123,45 @@ def get_supported(
313
123
:param abi: specify the exact abi you want valid
314
124
tags for, or None. If None, use the local interpreter abi.
315
125
"""
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
+ )
322
151
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
+ )
370
166
371
- return [ Tag ( * parts ) for parts in supported ]
167
+ return supported
0 commit comments