-
Notifications
You must be signed in to change notification settings - Fork 90
ENH: implement support for build-details.json (PEP 739) (continued) #829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
08a6811
ce79e07
30b1223
d2ab874
ee06ebf
10febcc
055b7d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,27 @@ | |||||||||||
| import struct | ||||||||||||
| import sys | ||||||||||||
| import sysconfig | ||||||||||||
| import typing | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| if typing.TYPE_CHECKING: # pragma: no cover | ||||||||||||
| from typing import TypedDict | ||||||||||||
|
|
||||||||||||
| class _Abi(TypedDict): | ||||||||||||
| extension_suffix: str | ||||||||||||
|
|
||||||||||||
| class _ImplementationVersion(TypedDict): | ||||||||||||
| major: int | ||||||||||||
| minor: int | ||||||||||||
|
|
||||||||||||
| class _Implementation(TypedDict): | ||||||||||||
| name: str | ||||||||||||
| version: _ImplementationVersion | ||||||||||||
|
|
||||||||||||
| class BuildDetails(TypedDict): | ||||||||||||
| abi: _Abi | ||||||||||||
| implementation: _Implementation | ||||||||||||
| platform: str | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| # https://peps.python.org/pep-0425/#python-tag | ||||||||||||
|
|
@@ -24,22 +45,100 @@ | |||||||||||
| _32_BIT_INTERPRETER = struct.calcsize('P') == 4 | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def get_interpreter_tag() -> str: | ||||||||||||
| name = sys.implementation.name | ||||||||||||
| def _get_macosx_platform() -> str: | ||||||||||||
| ver, _, arch = platform.mac_ver() | ||||||||||||
| major, minor = map(int, ver.split('.')[:2]) | ||||||||||||
|
|
||||||||||||
| # Python built with older macOS SDK on macOS 11, reports an | ||||||||||||
| # nonexistent macOS 10.16 version instead of the real version. | ||||||||||||
| # | ||||||||||||
| # The packaging module introduced a workaround | ||||||||||||
| # https://github.com/pypa/packaging/commit/67c4a2820c549070bbfc4bfbf5e2a250075048da | ||||||||||||
| # | ||||||||||||
| # This results in packaging versions up to 21.3 generating | ||||||||||||
| # platform tags like "macosx_10_16_x86_64" and later versions | ||||||||||||
| # generating "macosx_11_0_x86_64". Using the latter would be more | ||||||||||||
| # correct but prevents the resulting wheel from being installed on | ||||||||||||
| # systems using packaging 21.3 or earlier (pip 22.3 or earlier). | ||||||||||||
| # | ||||||||||||
| # Fortunately packaging versions carrying the workaround still | ||||||||||||
| # accepts "macosx_10_16_x86_64" as a compatible platform tag. We | ||||||||||||
| # can therefore ignore the issue and generate the slightly | ||||||||||||
| # incorrect tag. | ||||||||||||
|
Comment on lines
+52
to
+67
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about the code this comments refers to?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding is that this comment is saying that we don't need any code to handle that case. It was followed by empty line, then another comment. I've figured out it's better to move it where we get the platform, since it applied to what the system gives us. |
||||||||||||
|
|
||||||||||||
| if _32_BIT_INTERPRETER: | ||||||||||||
| # 32-bit Python running on a 64-bit kernel. | ||||||||||||
| if arch == 'ppc64': | ||||||||||||
| arch = 'ppc' | ||||||||||||
| if arch == 'x86_64': | ||||||||||||
| arch = 'i386' | ||||||||||||
|
|
||||||||||||
| return f'macosx-{major}.{minor}-{arch}' | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def _get_ios_platform() -> str: | ||||||||||||
| ver = platform.ios_ver().release | ||||||||||||
| major, minor = map(int, ver.split('.')[:2]) | ||||||||||||
|
|
||||||||||||
| # Although _multiarch is an internal implementation detail, it's a core part | ||||||||||||
| # of how CPython is implemented on iOS; this attribute is also relied upon | ||||||||||||
| # by `packaging` as part of tag determination. | ||||||||||||
| multiarch = sys.implementation._multiarch.replace('-', '_') | ||||||||||||
|
|
||||||||||||
| return f'ios-{major}.{minor}-{multiarch}' | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def introspect_build_details() -> BuildDetails: | ||||||||||||
| platform = sysconfig.get_platform() | ||||||||||||
| if platform.startswith('macosx'): | ||||||||||||
| platform = _get_macosx_platform() | ||||||||||||
| elif platform.startswith('ios'): | ||||||||||||
| platform = _get_ios_platform() | ||||||||||||
| elif _32_BIT_INTERPRETER: | ||||||||||||
| # 32-bit Python running on a 64-bit kernel. | ||||||||||||
| if platform == 'linux-x86_64': | ||||||||||||
| platform = 'linux_i686' | ||||||||||||
| if platform == 'linux-aarch64': | ||||||||||||
| platform = 'linux_armv7l' | ||||||||||||
|
|
||||||||||||
| return { | ||||||||||||
| 'abi': { | ||||||||||||
| # PyPy reports a $SOABI that does not agree with $EXT_SUFFIX. | ||||||||||||
| # Using $EXT_SUFFIX will not break when PyPy will fix this. | ||||||||||||
| # See https://foss.heptapod.net/pypy/pypy/-/issues/3816 and | ||||||||||||
| # https://github.com/pypa/packaging/pull/607. | ||||||||||||
|
Comment on lines
+106
to
+109
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this comment belongs here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's been here: Lines 38 to 42 in 7c78da3
and it seems related to why we're using |
||||||||||||
| 'extension_suffix': str(sysconfig.get_config_var('EXT_SUFFIX')), | ||||||||||||
| }, | ||||||||||||
| 'implementation': { | ||||||||||||
| 'name': sys.implementation.name, | ||||||||||||
| 'version': { | ||||||||||||
| 'major': sys.version_info.major, | ||||||||||||
| 'minor': sys.version_info.minor, | ||||||||||||
| }, | ||||||||||||
| }, | ||||||||||||
| 'platform': platform, | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def get_interpreter_tag(build_details: BuildDetails) -> str: | ||||||||||||
| name = build_details['implementation']['name'] | ||||||||||||
| _v = build_details['implementation']['version'] | ||||||||||||
| major = _v['major'] | ||||||||||||
| minor = _v['minor'] | ||||||||||||
| name = INTERPRETERS.get(name, name) | ||||||||||||
| version = sys.version_info | ||||||||||||
| return f'{name}{version[0]}{version[1]}' | ||||||||||||
| return f'{name}{major}{minor}' | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def get_abi_tag() -> str: | ||||||||||||
| def get_abi_tag(build_details: BuildDetails) -> str: | ||||||||||||
| # The best solution to obtain the Python ABI is to parse the | ||||||||||||
| # $SOABI or $EXT_SUFFIX sysconfig variables as defined in PEP-314. | ||||||||||||
|
|
||||||||||||
| # PyPy reports a $SOABI that does not agree with $EXT_SUFFIX. | ||||||||||||
| # Using $EXT_SUFFIX will not break when PyPy will fix this. | ||||||||||||
| # See https://foss.heptapod.net/pypy/pypy/-/issues/3816 and | ||||||||||||
| # https://github.com/pypa/packaging/pull/607. | ||||||||||||
| empty, abi, ext = str(sysconfig.get_config_var('EXT_SUFFIX')).split('.') | ||||||||||||
| ext_suffix = build_details['abi']['extension_suffix'] | ||||||||||||
| empty, abi, ext = ext_suffix.split('.') | ||||||||||||
|
|
||||||||||||
| # The packaging module initially based his understanding of the | ||||||||||||
| # $SOABI variable on the inconsistent value reported by PyPy, and | ||||||||||||
|
|
@@ -60,8 +159,9 @@ def get_abi_tag() -> str: | |||||||||||
| return abi.replace('.', '_').replace('-', '_') | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def _get_macosx_platform_tag() -> str: | ||||||||||||
| ver, _, arch = platform.mac_ver() | ||||||||||||
| def _get_macosx_platform_tag(platform: str) -> str: | ||||||||||||
| name, ver, arch = platform.split('-', 2) | ||||||||||||
| assert name == 'macosx' | ||||||||||||
|
|
||||||||||||
| # Override the architecture with the one provided in the | ||||||||||||
| # _PYTHON_HOST_PLATFORM environment variable. This environment | ||||||||||||
|
|
@@ -81,24 +181,7 @@ def _get_macosx_platform_tag() -> str: | |||||||||||
| parts = os.environ.get('MACOSX_DEPLOYMENT_TARGET', '').split('.')[:2] | ||||||||||||
| version = tuple(map(int, parts + ['0'] * (2 - len(parts)))) | ||||||||||||
| except ValueError: | ||||||||||||
| version = tuple(map(int, ver.split('.')))[:2] | ||||||||||||
|
|
||||||||||||
| # Python built with older macOS SDK on macOS 11, reports an | ||||||||||||
| # nonexistent macOS 10.16 version instead of the real version. | ||||||||||||
| # | ||||||||||||
| # The packaging module introduced a workaround | ||||||||||||
| # https://github.com/pypa/packaging/commit/67c4a2820c549070bbfc4bfbf5e2a250075048da | ||||||||||||
| # | ||||||||||||
| # This results in packaging versions up to 21.3 generating | ||||||||||||
| # platform tags like "macosx_10_16_x86_64" and later versions | ||||||||||||
| # generating "macosx_11_0_x86_64". Using the latter would be more | ||||||||||||
| # correct but prevents the resulting wheel from being installed on | ||||||||||||
| # systems using packaging 21.3 or earlier (pip 22.3 or earlier). | ||||||||||||
| # | ||||||||||||
| # Fortunately packaging versions carrying the workaround still | ||||||||||||
| # accepts "macosx_10_16_x86_64" as a compatible platform tag. We | ||||||||||||
| # can therefore ignore the issue and generate the slightly | ||||||||||||
| # incorrect tag. | ||||||||||||
| version = tuple(map(int, ver.split('.')[:2])) | ||||||||||||
|
|
||||||||||||
| # The minimum macOS ABI version on arm64 is 11.0. The macOS SDK | ||||||||||||
| # on arm64 silently bumps any compatibility version specified via | ||||||||||||
|
|
@@ -120,53 +203,39 @@ def _get_macosx_platform_tag() -> str: | |||||||||||
| # the patch level. Reset the patch level to zero. | ||||||||||||
| minor = 0 | ||||||||||||
|
|
||||||||||||
| if _32_BIT_INTERPRETER: | ||||||||||||
| # 32-bit Python running on a 64-bit kernel. | ||||||||||||
| if arch == 'ppc64': | ||||||||||||
| arch = 'ppc' | ||||||||||||
| if arch == 'x86_64': | ||||||||||||
| arch = 'i386' | ||||||||||||
|
|
||||||||||||
| return f'macosx_{major}_{minor}_{arch}' | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def _get_ios_platform_tag() -> str: | ||||||||||||
| def _get_ios_platform_tag(platform: str) -> str: | ||||||||||||
| name, version, multiarch = platform.split('-', 2) | ||||||||||||
| assert name == 'ios' | ||||||||||||
|
|
||||||||||||
| # Override the iOS version if one is provided via the | ||||||||||||
| # IPHONEOS_DEPLOYMENT_TARGET environment variable. | ||||||||||||
| try: | ||||||||||||
| parts = os.environ.get('IPHONEOS_DEPLOYMENT_TARGET', '').split('.')[:2] | ||||||||||||
| version = tuple(map(int, parts + ['0'] * (2 - len(parts)))) | ||||||||||||
| version = '.'.join(map(int, parts + ['0'] * (2 - len(parts)))) | ||||||||||||
| except ValueError: | ||||||||||||
| version = tuple(map(int, platform.ios_ver().release.split('.')))[:2] # type: ignore[attr-defined] | ||||||||||||
|
|
||||||||||||
| # Although _multiarch is an internal implementation detail, it's a core part | ||||||||||||
| # of how CPython is implemented on iOS; this attribute is also relied upon | ||||||||||||
| # by `packaging` as part of tag determination. | ||||||||||||
| multiarch = sys.implementation._multiarch.replace('-', '_') | ||||||||||||
| pass | ||||||||||||
|
|
||||||||||||
| return f'ios_{version[0]}_{version[1]}_{multiarch}' | ||||||||||||
| return f'ios_{version.replace(".", "_")}_{multiarch.replace("-", "_")}' | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def get_platform_tag() -> str: | ||||||||||||
| platform = sysconfig.get_platform() | ||||||||||||
| def get_platform_tag(build_details: BuildDetails) -> str: | ||||||||||||
| platform = build_details['platform'] | ||||||||||||
| if platform.startswith('macosx'): | ||||||||||||
| return _get_macosx_platform_tag() | ||||||||||||
| return _get_macosx_platform_tag(platform) | ||||||||||||
| if platform.startswith('ios'): | ||||||||||||
| return _get_ios_platform_tag() | ||||||||||||
| if _32_BIT_INTERPRETER: | ||||||||||||
| # 32-bit Python running on a 64-bit kernel. | ||||||||||||
| if platform == 'linux-x86_64': | ||||||||||||
| return 'linux_i686' | ||||||||||||
| if platform == 'linux-aarch64': | ||||||||||||
| return 'linux_armv7l' | ||||||||||||
| return _get_ios_platform_tag(platform) | ||||||||||||
| return platform.replace('-', '_').replace('.', '_').lower() | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| class Tag: | ||||||||||||
| def __init__(self, interpreter: str | None = None, abi: str | None = None, platform: str | None = None): | ||||||||||||
| self.interpreter = interpreter or get_interpreter_tag() | ||||||||||||
| self.abi = abi or get_abi_tag() | ||||||||||||
| self.platform = platform or get_platform_tag() | ||||||||||||
| def __init__(self, interpreter: str | None = None, abi: str | None = None, platform: str | None = None, | ||||||||||||
| *, build_details: BuildDetails): | ||||||||||||
| self.interpreter = interpreter or get_interpreter_tag(build_details) | ||||||||||||
| self.abi = abi or get_abi_tag(build_details) | ||||||||||||
| self.platform = platform or get_platform_tag(build_details) | ||||||||||||
|
|
||||||||||||
| def __str__(self) -> str: | ||||||||||||
| return f'{self.interpreter}-{self.abi}-{self.platform}' | ||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This annotation is now wrong:
self._build_detailscannot beNone. Also, now that we use this attribute to always store information on the interpreter we can consider to name it in a less confusing way (now, at a first look it could seem that this attribute contains information about the build of the Meson project). Maybeself._infois not too bad of a name.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is
Nonetemporarily. Unless you want me to undo the optimization and set the default value unconditionally, then override ifpython_build_configarg is provided.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind renaming it, though
infosounds a bit unclear. Maybetag_info? Should I also rename the classes and the argument elsewhere?