-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Joe Rickerby <joerick@mac.com>
- Loading branch information
Showing
6 changed files
with
532 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,4 @@ Reference Guide | |
wheel_convert | ||
wheel_unpack | ||
wheel_pack | ||
wheel_tags |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
wheel tags | ||
========== | ||
|
||
Usage | ||
----- | ||
|
||
:: | ||
|
||
wheel tags [-h] [--remove] [--python-tag TAG] [--abi-tag TAG] [--platform-tag TAG] [--build NUMBER] WHEEL [...] | ||
|
||
Description | ||
----------- | ||
|
||
Make a new wheel with given tags from and existing wheel. Any tags left | ||
unspecified will remain the same. Multiple tags are separated by a "." Starting | ||
with a "+" will append to the existing tags. Starting with a "-" will remove a | ||
tag. Be sure to use the equals syntax on the shell so that it does not get | ||
parsed as an extra option, such as ``--python-tag=-py2``. The original file | ||
will remain unless ``--remove`` is given. The output filename(s) will be | ||
displayed on stdout for further processing. | ||
|
||
|
||
Options | ||
------- | ||
|
||
.. option:: --remove | ||
|
||
Remove the original wheel, keeping only the retagged wheel. | ||
|
||
.. option:: --python-tag=TAG | ||
|
||
Override the python tag (prepend with "+" to append, "-" to remove). | ||
Multiple tags can be separated with a dot. | ||
|
||
.. option:: --abi-tag=TAG | ||
|
||
Override the abi tag (prepend with "+" to append, "-" to remove). | ||
Multiple tags can be separated with a dot. | ||
|
||
.. option:: --platform-tag=TAG | ||
|
||
Override the platform tag (prepend with "+" to append, "-" to remove). | ||
Multiple tags can be separated with a dot. | ||
|
||
.. option:: --build=NUMBER | ||
|
||
Specify a build number. | ||
|
||
Examples | ||
-------- | ||
|
||
* Replace a wheel's Python specific tags with generic tags (if no Python extensions are present, for example):: | ||
|
||
$ wheel tags --python-tag=py2.py3 --abi-tag=none cmake-3.20.2-cp39-cp39-win_amd64.whl | ||
cmake-3.20.2-py2.py3-none-win_amd64.whl | ||
|
||
* Add compatibility tags for macOS universal wheels and older pips:: | ||
|
||
$ wheel tags \ | ||
--platform-tag=+macosx_10_9_x86_64.macosx_11_0_arm64 \ | ||
ninja-1.11.1-py2.py3-none-macosx_10_9_universal2.whl | ||
ninja-1.11.1-py2.py3-none-macosx_10_9_universal2.macosx_10_9_x86_64.macosx_11_0_arm64.whl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
from __future__ import annotations | ||
|
||
import itertools | ||
import os | ||
from collections.abc import Iterable | ||
|
||
from ..wheelfile import WheelFile | ||
from .pack import read_tags, set_build_number | ||
|
||
|
||
def _compute_tags(original_tags: Iterable[str], new_tags: str | None) -> set[str]: | ||
"""Add or replace tags. Supports dot-separated tags""" | ||
if new_tags is None: | ||
return set(original_tags) | ||
|
||
if new_tags.startswith("+"): | ||
return {*original_tags, *new_tags[1:].split(".")} | ||
|
||
if new_tags.startswith("-"): | ||
return set(original_tags) - set(new_tags[1:].split(".")) | ||
|
||
return set(new_tags.split(".")) | ||
|
||
|
||
def tags( | ||
wheel: str, | ||
python_tags: str | None = None, | ||
abi_tags: str | None = None, | ||
platform_tags: str | None = None, | ||
build_number: int | None = None, | ||
remove: bool = False, | ||
) -> str: | ||
"""Change the tags on a wheel file. | ||
The tags are left unchanged if they are not specified. To specify "none", | ||
use ["none"]. To append to the previous tags, a tag should start with a | ||
"+". If a tag starts with "-", it will be removed from existing tags. | ||
Processing is done left to right. | ||
:param wheel: The paths to the wheels | ||
:param python_tags: The Python tags to set | ||
:param abi_tags: The ABI tags to set | ||
:param platform_tags: The platform tags to set | ||
:param build_number: The build number to set | ||
:param remove: Remove the original wheel | ||
""" | ||
with WheelFile(wheel, "r") as f: | ||
assert f.filename, f"{f.filename} must be available" | ||
|
||
wheel_info = f.read(f.dist_info_path + "/WHEEL") | ||
|
||
original_wheel_name = os.path.basename(f.filename) | ||
namever = f.parsed_filename.group("namever") | ||
build = f.parsed_filename.group("build") | ||
original_python_tags = f.parsed_filename.group("pyver").split(".") | ||
original_abi_tags = f.parsed_filename.group("abi").split(".") | ||
original_plat_tags = f.parsed_filename.group("plat").split(".") | ||
|
||
tags, existing_build_number = read_tags(wheel_info) | ||
|
||
impls = {tag.split("-")[0] for tag in tags} | ||
abivers = {tag.split("-")[1] for tag in tags} | ||
platforms = {tag.split("-")[2] for tag in tags} | ||
|
||
if impls != set(original_python_tags): | ||
msg = f"Wheel internal tags {impls!r} != filename tags {original_python_tags!r}" | ||
raise AssertionError(msg) | ||
|
||
if abivers != set(original_abi_tags): | ||
msg = f"Wheel internal tags {abivers!r} != filename tags {original_abi_tags!r}" | ||
raise AssertionError(msg) | ||
|
||
if platforms != set(original_plat_tags): | ||
msg = ( | ||
f"Wheel internal tags {platforms!r} != filename tags {original_plat_tags!r}" | ||
) | ||
raise AssertionError(msg) | ||
|
||
if existing_build_number != build: | ||
msg = ( | ||
f"Incorrect filename '{build}' " | ||
"& *.dist-info/WHEEL '{existing_build_number}' build numbers" | ||
) | ||
raise AssertionError(msg) | ||
|
||
# Start changing as needed | ||
if build_number is not None: | ||
build = str(build_number) | ||
|
||
final_python_tags = sorted(_compute_tags(original_python_tags, python_tags)) | ||
final_abi_tags = sorted(_compute_tags(original_abi_tags, abi_tags)) | ||
final_plat_tags = sorted(_compute_tags(original_plat_tags, platform_tags)) | ||
|
||
final_tags = [ | ||
namever, | ||
".".join(final_python_tags), | ||
".".join(final_abi_tags), | ||
".".join(final_plat_tags), | ||
] | ||
if build: | ||
final_tags.insert(1, build) | ||
|
||
final_wheel_name = "-".join(final_tags) + ".whl" | ||
|
||
if original_wheel_name != final_wheel_name: | ||
tags = [ | ||
f"{a}-{b}-{c}" | ||
for a, b, c in itertools.product( | ||
final_python_tags, final_abi_tags, final_plat_tags | ||
) | ||
] | ||
|
||
original_wheel_path = os.path.join( | ||
os.path.dirname(f.filename), original_wheel_name | ||
) | ||
final_wheel_path = os.path.join(os.path.dirname(f.filename), final_wheel_name) | ||
|
||
with WheelFile(original_wheel_path, "r") as fin, WheelFile( | ||
final_wheel_path, "w" | ||
) as fout: | ||
fout.comment = fin.comment # preserve the comment | ||
for item in fin.infolist(): | ||
if item.filename == f.dist_info_path + "/RECORD": | ||
continue | ||
if item.filename == f.dist_info_path + "/WHEEL": | ||
content = fin.read(item) | ||
content = set_tags(content, tags) | ||
content = set_build_number(content, build) | ||
fout.writestr(item, content) | ||
else: | ||
fout.writestr(item, fin.read(item)) | ||
|
||
if remove: | ||
os.remove(original_wheel_path) | ||
|
||
return final_wheel_name | ||
|
||
|
||
def set_tags(in_string: bytes, tags: Iterable[str]) -> bytes: | ||
"""Set the tags in the .dist-info/WHEEL file contents. | ||
:param in_string: The string to modify. | ||
:param tags: The tags to set. | ||
""" | ||
|
||
lines = [line for line in in_string.splitlines() if not line.startswith(b"Tag:")] | ||
for tag in tags: | ||
lines.append(b"Tag: " + tag.encode("ascii")) | ||
in_string = b"\r\n".join(lines) + b"\r\n" | ||
|
||
return in_string |
Oops, something went wrong.