Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pep425tags.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. """Generate and work with PEP 425 Compatibility Tags."""
  2. from __future__ import absolute_import
  3. import distutils.util
  4. import logging
  5. import platform
  6. import re
  7. import sys
  8. import sysconfig
  9. import warnings
  10. from collections import OrderedDict
  11. import pip._internal.utils.glibc
  12. from pip._internal.utils.compat import get_extension_suffixes
  13. logger = logging.getLogger(__name__)
  14. _osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)')
  15. def get_config_var(var):
  16. try:
  17. return sysconfig.get_config_var(var)
  18. except IOError as e: # Issue #1074
  19. warnings.warn("{}".format(e), RuntimeWarning)
  20. return None
  21. def get_abbr_impl():
  22. """Return abbreviated implementation name."""
  23. if hasattr(sys, 'pypy_version_info'):
  24. pyimpl = 'pp'
  25. elif sys.platform.startswith('java'):
  26. pyimpl = 'jy'
  27. elif sys.platform == 'cli':
  28. pyimpl = 'ip'
  29. else:
  30. pyimpl = 'cp'
  31. return pyimpl
  32. def get_impl_ver():
  33. """Return implementation version."""
  34. impl_ver = get_config_var("py_version_nodot")
  35. if not impl_ver or get_abbr_impl() == 'pp':
  36. impl_ver = ''.join(map(str, get_impl_version_info()))
  37. return impl_ver
  38. def get_impl_version_info():
  39. """Return sys.version_info-like tuple for use in decrementing the minor
  40. version."""
  41. if get_abbr_impl() == 'pp':
  42. # as per https://github.com/pypa/pip/issues/2882
  43. return (sys.version_info[0], sys.pypy_version_info.major,
  44. sys.pypy_version_info.minor)
  45. else:
  46. return sys.version_info[0], sys.version_info[1]
  47. def get_impl_tag():
  48. """
  49. Returns the Tag for this specific implementation.
  50. """
  51. return "{}{}".format(get_abbr_impl(), get_impl_ver())
  52. def get_flag(var, fallback, expected=True, warn=True):
  53. """Use a fallback method for determining SOABI flags if the needed config
  54. var is unset or unavailable."""
  55. val = get_config_var(var)
  56. if val is None:
  57. if warn:
  58. logger.debug("Config variable '%s' is unset, Python ABI tag may "
  59. "be incorrect", var)
  60. return fallback()
  61. return val == expected
  62. def get_abi_tag():
  63. """Return the ABI tag based on SOABI (if available) or emulate SOABI
  64. (CPython 2, PyPy)."""
  65. soabi = get_config_var('SOABI')
  66. impl = get_abbr_impl()
  67. if not soabi and impl in {'cp', 'pp'} and hasattr(sys, 'maxunicode'):
  68. d = ''
  69. m = ''
  70. u = ''
  71. if get_flag('Py_DEBUG',
  72. lambda: hasattr(sys, 'gettotalrefcount'),
  73. warn=(impl == 'cp')):
  74. d = 'd'
  75. if get_flag('WITH_PYMALLOC',
  76. lambda: impl == 'cp',
  77. warn=(impl == 'cp')):
  78. m = 'm'
  79. if get_flag('Py_UNICODE_SIZE',
  80. lambda: sys.maxunicode == 0x10ffff,
  81. expected=4,
  82. warn=(impl == 'cp' and
  83. sys.version_info < (3, 3))) \
  84. and sys.version_info < (3, 3):
  85. u = 'u'
  86. abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u)
  87. elif soabi and soabi.startswith('cpython-'):
  88. abi = 'cp' + soabi.split('-')[1]
  89. elif soabi:
  90. abi = soabi.replace('.', '_').replace('-', '_')
  91. else:
  92. abi = None
  93. return abi
  94. def _is_running_32bit():
  95. return sys.maxsize == 2147483647
  96. def get_platform():
  97. """Return our platform name 'win32', 'linux_x86_64'"""
  98. if sys.platform == 'darwin':
  99. # distutils.util.get_platform() returns the release based on the value
  100. # of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may
  101. # be significantly older than the user's current machine.
  102. release, _, machine = platform.mac_ver()
  103. split_ver = release.split('.')
  104. if machine == "x86_64" and _is_running_32bit():
  105. machine = "i386"
  106. elif machine == "ppc64" and _is_running_32bit():
  107. machine = "ppc"
  108. return 'macosx_{}_{}_{}'.format(split_ver[0], split_ver[1], machine)
  109. # XXX remove distutils dependency
  110. result = distutils.util.get_platform().replace('.', '_').replace('-', '_')
  111. if result == "linux_x86_64" and _is_running_32bit():
  112. # 32 bit Python program (running on a 64 bit Linux): pip should only
  113. # install and run 32 bit compiled extensions in that case.
  114. result = "linux_i686"
  115. return result
  116. def is_manylinux1_compatible():
  117. # Only Linux, and only x86-64 / i686
  118. if get_platform() not in {"linux_x86_64", "linux_i686"}:
  119. return False
  120. # Check for presence of _manylinux module
  121. try:
  122. import _manylinux
  123. return bool(_manylinux.manylinux1_compatible)
  124. except (ImportError, AttributeError):
  125. # Fall through to heuristic check below
  126. pass
  127. # Check glibc version. CentOS 5 uses glibc 2.5.
  128. return pip._internal.utils.glibc.have_compatible_glibc(2, 5)
  129. def get_darwin_arches(major, minor, machine):
  130. """Return a list of supported arches (including group arches) for
  131. the given major, minor and machine architecture of an macOS machine.
  132. """
  133. arches = []
  134. def _supports_arch(major, minor, arch):
  135. # Looking at the application support for macOS versions in the chart
  136. # provided by https://en.wikipedia.org/wiki/OS_X#Versions it appears
  137. # our timeline looks roughly like:
  138. #
  139. # 10.0 - Introduces ppc support.
  140. # 10.4 - Introduces ppc64, i386, and x86_64 support, however the ppc64
  141. # and x86_64 support is CLI only, and cannot be used for GUI
  142. # applications.
  143. # 10.5 - Extends ppc64 and x86_64 support to cover GUI applications.
  144. # 10.6 - Drops support for ppc64
  145. # 10.7 - Drops support for ppc
  146. #
  147. # Given that we do not know if we're installing a CLI or a GUI
  148. # application, we must be conservative and assume it might be a GUI
  149. # application and behave as if ppc64 and x86_64 support did not occur
  150. # until 10.5.
  151. #
  152. # Note: The above information is taken from the "Application support"
  153. # column in the chart not the "Processor support" since I believe
  154. # that we care about what instruction sets an application can use
  155. # not which processors the OS supports.
  156. if arch == 'ppc':
  157. return (major, minor) <= (10, 5)
  158. if arch == 'ppc64':
  159. return (major, minor) == (10, 5)
  160. if arch == 'i386':
  161. return (major, minor) >= (10, 4)
  162. if arch == 'x86_64':
  163. return (major, minor) >= (10, 5)
  164. if arch in groups:
  165. for garch in groups[arch]:
  166. if _supports_arch(major, minor, garch):
  167. return True
  168. return False
  169. groups = OrderedDict([
  170. ("fat", ("i386", "ppc")),
  171. ("intel", ("x86_64", "i386")),
  172. ("fat64", ("x86_64", "ppc64")),
  173. ("fat32", ("x86_64", "i386", "ppc")),
  174. ])
  175. if _supports_arch(major, minor, machine):
  176. arches.append(machine)
  177. for garch in groups:
  178. if machine in groups[garch] and _supports_arch(major, minor, garch):
  179. arches.append(garch)
  180. arches.append('universal')
  181. return arches
  182. def get_supported(versions=None, noarch=False, platform=None,
  183. impl=None, abi=None):
  184. """Return a list of supported tags for each version specified in
  185. `versions`.
  186. :param versions: a list of string versions, of the form ["33", "32"],
  187. or None. The first version will be assumed to support our ABI.
  188. :param platform: specify the exact platform you want valid
  189. tags for, or None. If None, use the local system platform.
  190. :param impl: specify the exact implementation you want valid
  191. tags for, or None. If None, use the local interpreter impl.
  192. :param abi: specify the exact abi you want valid
  193. tags for, or None. If None, use the local interpreter abi.
  194. """
  195. supported = []
  196. # Versions must be given with respect to the preference
  197. if versions is None:
  198. versions = []
  199. version_info = get_impl_version_info()
  200. major = version_info[:-1]
  201. # Support all previous minor Python versions.
  202. for minor in range(version_info[-1], -1, -1):
  203. versions.append(''.join(map(str, major + (minor,))))
  204. impl = impl or get_abbr_impl()
  205. abis = []
  206. abi = abi or get_abi_tag()
  207. if abi:
  208. abis[0:0] = [abi]
  209. abi3s = set()
  210. for suffix in get_extension_suffixes():
  211. if suffix.startswith('.abi'):
  212. abi3s.add(suffix.split('.', 2)[1])
  213. abis.extend(sorted(list(abi3s)))
  214. abis.append('none')
  215. if not noarch:
  216. arch = platform or get_platform()
  217. if arch.startswith('macosx'):
  218. # support macosx-10.6-intel on macosx-10.9-x86_64
  219. match = _osx_arch_pat.match(arch)
  220. if match:
  221. name, major, minor, actual_arch = match.groups()
  222. tpl = '{}_{}_%i_%s'.format(name, major)
  223. arches = []
  224. for m in reversed(range(int(minor) + 1)):
  225. for a in get_darwin_arches(int(major), m, actual_arch):
  226. arches.append(tpl % (m, a))
  227. else:
  228. # arch pattern didn't match (?!)
  229. arches = [arch]
  230. elif platform is None and is_manylinux1_compatible():
  231. arches = [arch.replace('linux', 'manylinux1'), arch]
  232. else:
  233. arches = [arch]
  234. # Current version, current API (built specifically for our Python):
  235. for abi in abis:
  236. for arch in arches:
  237. supported.append(('%s%s' % (impl, versions[0]), abi, arch))
  238. # abi3 modules compatible with older version of Python
  239. for version in versions[1:]:
  240. # abi3 was introduced in Python 3.2
  241. if version in {'31', '30'}:
  242. break
  243. for abi in abi3s: # empty set if not Python 3
  244. for arch in arches:
  245. supported.append(("%s%s" % (impl, version), abi, arch))
  246. # Has binaries, does not use the Python API:
  247. for arch in arches:
  248. supported.append(('py%s' % (versions[0][0]), 'none', arch))
  249. # No abi / arch, but requires our implementation:
  250. supported.append(('%s%s' % (impl, versions[0]), 'none', 'any'))
  251. # Tagged specifically as being cross-version compatible
  252. # (with just the major version specified)
  253. supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any'))
  254. # No abi / arch, generic Python
  255. for i, version in enumerate(versions):
  256. supported.append(('py%s' % (version,), 'none', 'any'))
  257. if i == 0:
  258. supported.append(('py%s' % (version[0]), 'none', 'any'))
  259. return supported
  260. implementation_tag = get_impl_tag()