From be6617da5a1b08d2438a474c5b9fb1a433ac9dbb Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 7 Oct 2021 17:10:27 -0400 Subject: [PATCH 01/12] feat: adding tags CLI interface usage: wheel tags [-h] [--remove] [--python-tag TAG] [--abi-tag TAG] [--platform-tag TAG] [--build NUMBER] [wheel ...] Make a new wheel with given tags. Any tags unspecified will remain the same. Separate multiple tags with a dot. Starting with a dot will append to the existing tags. The original file will remain unless --remove is given. The output file(s) will be displayed on stdout. positional arguments: wheel Existing wheel(s) to retag options: -h, --help Show this help message and exit --remove Remove the original files, keeping only the renamed ones --python-tag TAG Specify an interpreter tag(s) --abi-tag TAG Specify an ABI tag(s) --platform-tag TAG Specify a platform tag(s) --build NUMBER Specify a build number --- src/wheel/cli/__init__.py | 43 ++++++++++ src/wheel/cli/tags.py | 155 ++++++++++++++++++++++++++++++++++ tests/cli/test_tags.py | 170 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 src/wheel/cli/tags.py create mode 100644 tests/cli/test_tags.py diff --git a/src/wheel/cli/__init__.py b/src/wheel/cli/__init__.py index c0fb8c44..663189c4 100644 --- a/src/wheel/cli/__init__.py +++ b/src/wheel/cli/__init__.py @@ -31,12 +31,34 @@ def convert_f(args): convert(args.files, args.dest_dir, args.verbose) +def tags_f(args): + from .tags import tags + + for name in tags( + args.wheel, + args.python_tag and args.python_tag.split("."), + args.abi_tag and args.abi_tag.split("."), + args.platform_tag and args.platform_tag.split("."), + args.build, + args.remove, + ): + print(name) + + def version_f(args): from .. import __version__ print("wheel %s" % __version__) +TAGS_HELP = """\ +Make a new wheel with given tags. Any tags unspecified will remain the same. +Separate multiple tags with a dot. Starting with a dot will append to the +existing tags. The original file will remain unless --remove is given. The +output file(s) will be displayed on stdout. +""" + + def parser(): p = argparse.ArgumentParser() s = p.add_subparsers(help="commands") @@ -72,6 +94,27 @@ def parser(): convert_parser.add_argument("--verbose", "-v", action="store_true") convert_parser.set_defaults(func=convert_f) + tags_parser = s.add_parser( + "tags", help="Add or replace the tags on a wheel", description=TAGS_HELP + ) + tags_parser.add_argument("wheel", nargs="*", help="Existing wheel(s) to retag") + tags_parser.add_argument( + "--remove", + action="store_true", + help="Remove the original files, keeping only the renamed ones", + ) + tags_parser.add_argument( + "--python-tag", metavar="TAG", help="Specify an interpreter tag(s)" + ) + tags_parser.add_argument("--abi-tag", metavar="TAG", help="Specify an ABI tag(s)") + tags_parser.add_argument( + "--platform-tag", metavar="TAG", help="Specify a platform tag(s)" + ) + tags_parser.add_argument( + "--build", type=int, metavar="NUMBER", help="Specify a build number" + ) + tags_parser.set_defaults(func=tags_f) + version_parser = s.add_parser("version", help="Print version and exit") version_parser.set_defaults(func=version_f) diff --git a/src/wheel/cli/tags.py b/src/wheel/cli/tags.py new file mode 100644 index 00000000..ba8130b5 --- /dev/null +++ b/src/wheel/cli/tags.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +import itertools +import os +import shutil +import sys +import tempfile +from contextlib import contextmanager + +from ..wheelfile import WheelFile +from .pack import pack +from .unpack import unpack + +try: + from typing import Iterator +except ImportError: + pass + + +@contextmanager +def redirect_stdout(new_target): + old_target, sys.stdout = sys.stdout, new_target + try: + yield new_target + finally: + sys.stdout = old_target + + +@contextmanager +def temporary_directory(): + try: + dirname = tempfile.mkdtemp() + yield dirname + finally: + shutil.rmtree(dirname) + + +class InWheelCtx: + @property + def parsed_filename(self): + return self.wheel.parsed_filename + + @property + def filename(self): + return self.wheel.filename + + def __init__(self, wheel, tmpdir): + self.wheel = WheelFile(wheel) + self.tmpdir = tmpdir + self.build_number = None + # If dirname is unset, don't pack a new wheel + self.dirname = None + + def __enter__(self): + with redirect_stdout(sys.stderr): + unpack(self.wheel.filename, self.tmpdir) + self.wheel.__enter__() + return self + + def __exit__(self, *args): + self.wheel.__exit__(*args) + if self.dirname: + with redirect_stdout(sys.stderr): + pack( + os.path.join( + self.tmpdir, self.wheel.parsed_filename.group("namever") + ), + self.dirname, + self.build_number, + ) + + +def compute_tags(original_tags: list[str], new_tags: list[str] | None) -> list[str]: + """Add or replace tags.""" + + if not new_tags: + return original_tags + + if new_tags[0] == "": + return original_tags + new_tags[1:] + else: + return new_tags + + +def tags( + wheels: list[str], + python_tags: list[str] | None = None, + abi_tags: list[str] | None = None, + platform_tags: list[str] | None = None, + build_number: int | None = None, + remove: bool = False, +) -> Iterator[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, use ["", ...]. + + :param wheels: 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. + """ + + for wheel in wheels: + with temporary_directory() as tmpdir, InWheelCtx(wheel, tmpdir) as wfctx: + namever = wfctx.parsed_filename.group("namever") + build = wfctx.parsed_filename.group("build") + original_python_tags = wfctx.parsed_filename.group("pyver").split(".") + original_abi_tags = wfctx.parsed_filename.group("abi").split(".") + orignial_plat_tags = wfctx.parsed_filename.group("plat").split(".") + + if build_number is not None: + build = str(build_number) + + final_python_tags = compute_tags(original_python_tags, python_tags) + final_abi_tags = compute_tags(original_abi_tags, abi_tags) + final_plat_tags = compute_tags(orignial_plat_tags, platform_tags) + + final_tags = [ + ".".join(sorted(final_python_tags)), + ".".join(sorted(final_abi_tags)), + ".".join(sorted(final_plat_tags)), + ] + + if build: + final_tags.insert(0, build) + final_tags.insert(0, namever) + + original_wheel_name = os.path.basename(wfctx.filename) + final_wheel_name = "-".join(final_tags) + ".whl" + + if original_wheel_name != final_wheel_name: + + wheelinfo = os.path.join( + tmpdir, namever, wfctx.wheel.dist_info_path, "WHEEL" + ) + with open(wheelinfo, "rb+") as f: + lines = [line for line in f if not line.startswith(b"Tag:")] + for a, b, c in itertools.product( + final_python_tags, final_abi_tags, final_plat_tags + ): + lines.append(f"Tag: {a}-{b}-{c}\r\n".encode("ascii")) + f.seek(0) + f.truncate() + f.write(b"".join(lines)) + + wfctx.build_number = build + wfctx.dirname = os.path.dirname(wheel) + + if remove: + os.remove(wheel) + + yield final_wheel_name diff --git a/tests/cli/test_tags.py b/tests/cli/test_tags.py new file mode 100644 index 00000000..bc93e426 --- /dev/null +++ b/tests/cli/test_tags.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import os + +import py +import pytest + +from wheel.cli import parser +from wheel.cli.tags import tags +from wheel.wheelfile import WheelFile + +THISDIR = os.path.dirname(__file__) +TESTWHEEL_NAME = "test-1.0-py2.py3-none-any.whl" +TESTWHEEL_PATH = os.path.join(THISDIR, "..", "testdata", TESTWHEEL_NAME) + + +@pytest.fixture +def wheelpath(tmpdir): + fn = tmpdir.mkdir("wheels").join(TESTWHEEL_NAME) + py.path.local(TESTWHEEL_PATH).copy(fn) + return fn + + +def test_tags_no_args(wheelpath): + (newname,) = tags([str(wheelpath)]) + assert TESTWHEEL_NAME == newname + assert wheelpath.exists() + + +def test_python_tags(wheelpath): + (newname,) = tags([str(wheelpath)], python_tags=["py3"]) + assert TESTWHEEL_NAME.replace("py2.py3", "py3") == newname + output_file = wheelpath.dirpath(newname) + with WheelFile(str(output_file)) as f: + output = f.read(f.dist_info_path + "/WHEEL") + assert ( + output == b"Wheel-Version: 1.0\r\nGenerator: bdist_wheel (0.30.0)" + b"\r\nRoot-Is-Purelib: false\r\nTag: py3-none-any\r\n" + ) + output_file.remove() + + (newname,) = tags([str(wheelpath)], python_tags=["py2.py3"]) + assert TESTWHEEL_NAME == newname + + (newname,) = tags([str(wheelpath)], python_tags=["", "py4"], remove=True) + assert not wheelpath.exists() + assert TESTWHEEL_NAME.replace("py2.py3", "py2.py3.py4") == newname + output_file = wheelpath.dirpath(newname) + output_file.remove() + + +def test_abi_tags(wheelpath): + (newname,) = tags([str(wheelpath)], abi_tags=["cp33m"]) + assert TESTWHEEL_NAME.replace("none", "cp33m") == newname + output_file = wheelpath.dirpath(newname) + output_file.remove() + + (newname,) = tags([str(wheelpath)], abi_tags=["abi3", "cp33m"]) + assert TESTWHEEL_NAME.replace("none", "abi3.cp33m") == newname + output_file = wheelpath.dirpath(newname) + output_file.remove() + + (newname,) = tags([str(wheelpath)], abi_tags=["none"]) + assert TESTWHEEL_NAME == newname + + (newname,) = tags([str(wheelpath)], abi_tags=["", "abi3", "cp33m"], remove=True) + assert not wheelpath.exists() + assert TESTWHEEL_NAME.replace("none", "abi3.cp33m.none") == newname + output_file = wheelpath.dirpath(newname) + output_file.remove() + + +def test_plat_tags(wheelpath): + (newname,) = tags([str(wheelpath)], platform_tags=["linux_x86_64"]) + assert TESTWHEEL_NAME.replace("any", "linux_x86_64") == newname + output_file = wheelpath.dirpath(newname) + assert output_file.exists() + output_file.remove() + + (newname,) = tags([str(wheelpath)], platform_tags=["linux_x86_64", "win32"]) + assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname + output_file = wheelpath.dirpath(newname) + assert output_file.exists() + output_file.remove() + + (newname,) = tags([str(wheelpath)], platform_tags=["", "linux_x86_64", "win32"]) + assert TESTWHEEL_NAME.replace("any", "any.linux_x86_64.win32") == newname + output_file = wheelpath.dirpath(newname) + assert output_file.exists() + output_file.remove() + + (newname,) = tags([str(wheelpath)], platform_tags=["any"]) + assert TESTWHEEL_NAME == newname + + +def test_build_number(wheelpath): + (newname,) = tags([str(wheelpath)], build_number=1) + assert TESTWHEEL_NAME.replace("-py2", "-1-py2") == newname + output_file = wheelpath.dirpath(newname) + assert output_file.exists() + output_file.remove() + + +def test_multi_tags(wheelpath): + (newname,) = tags( + [str(wheelpath)], + platform_tags=["linux_x86_64"], + python_tags=["", "py4"], + build_number=1, + ) + assert "test-1.0-1-py2.py3.py4-none-linux_x86_64.whl" == newname + + output_file = wheelpath.dirpath(newname) + assert output_file.exists() + with WheelFile(str(output_file)) as f: + output = f.read(f.dist_info_path + "/WHEEL") + assert ( + output + == b"Wheel-Version: 1.0\r\nGenerator: bdist_wheel (0.30.0)\r\nRoot-Is-Purelib:" + b" false\r\nTag: py2-none-linux_x86_64\r\nTag: py3-none-linux_x86_64\r\nTag:" + b" py4-none-linux_x86_64\r\nBuild: 1\r\n" + ) + output_file.remove() + + +def test_tags_command(capsys, wheelpath): + args = [ + "tags", + "--python-tag", + "py3", + "--abi-tag", + "cp33m", + "--platform-tag", + "linux_x86_64", + "--build", + "7", + str(wheelpath), + ] + p = parser() + args = p.parse_args(args) + args.func(args) + assert wheelpath.exists() + + newname = capsys.readouterr().out.strip() + assert "test-1.0-7-py3-cp33m-linux_x86_64.whl" == newname + output_file = wheelpath.dirpath(newname) + output_file.remove() + + +def test_tags_command_del(capsys, wheelpath): + args = [ + "tags", + "--python-tag", + ".py4", + "--abi-tag", + "cp33m", + "--platform-tag", + "linux_x86_64", + "--remove", + str(wheelpath), + ] + p = parser() + args = p.parse_args(args) + args.func(args) + assert not wheelpath.exists() + + newname = capsys.readouterr().out.strip() + assert "test-1.0-py2.py3.py4-cp33m-linux_x86_64.whl" == newname + output_file = wheelpath.dirpath(newname) + output_file.remove() From d6d521f3a90925f0012243c963d77556c80a0d71 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 30 Dec 2021 00:41:17 -0500 Subject: [PATCH 02/12] refactor: custom impl --- src/wheel/cli/pack.py | 76 +++++++++++----- src/wheel/cli/tags.py | 197 ++++++++++++++++++++--------------------- tests/cli/test_tags.py | 29 ++++++ 3 files changed, 179 insertions(+), 123 deletions(-) diff --git a/src/wheel/cli/pack.py b/src/wheel/cli/pack.py index 1949d4cf..5d797918 100644 --- a/src/wheel/cli/pack.py +++ b/src/wheel/cli/pack.py @@ -37,13 +37,8 @@ def pack(directory: str, dest_dir: str, build_number: str | None): # Read the tags and the existing build number from .dist-info/WHEEL existing_build_number = None wheel_file_path = os.path.join(directory, dist_info_dir, "WHEEL") - with open(wheel_file_path) as f: - tags = [] - for line in f: - if line.startswith("Tag: "): - tags.append(line.split(" ")[1].rstrip()) - elif line.startswith("Build: "): - existing_build_number = line.split(" ")[1].rstrip() + with open(wheel_file_path, "rb") as f: + tags, existing_build_number = read_tags(f.read()) if not tags: raise WheelError( @@ -58,28 +53,16 @@ def pack(directory: str, dest_dir: str, build_number: str | None): name_version += "-" + build_number if build_number != existing_build_number: - replacement = ( - ("Build: %s\r\n" % build_number).encode("ascii") - if build_number - else b"" - ) with open(wheel_file_path, "rb+") as f: wheel_file_content = f.read() - wheel_file_content, num_replaced = BUILD_NUM_RE.subn( - replacement, wheel_file_content - ) - if not num_replaced: - wheel_file_content += replacement + wheel_file_content = set_build_number(wheel_file_content, build_number) f.seek(0) f.truncate() f.write(wheel_file_content) # Reassemble the tags for the wheel file - impls = sorted({tag.split("-")[0] for tag in tags}) - abivers = sorted({tag.split("-")[1] for tag in tags}) - platforms = sorted({tag.split("-")[2] for tag in tags}) - tagline = "-".join([".".join(impls), ".".join(abivers), ".".join(platforms)]) + tagline = compute_tagline(tags) # Repack the wheel wheel_path = os.path.join(dest_dir, f"{name_version}-{tagline}.whl") @@ -88,3 +71,54 @@ def pack(directory: str, dest_dir: str, build_number: str | None): wf.write_files(directory) print("OK") + + +def read_tags(input_str: bytes) -> tuple[list[str], str | None]: + """Read tags from a string. + + :param input_str: A string containing one or more tags, separated by spaces + :return: A list of tags and a list of build tags + """ + + tags = [] + existing_build_number = None + for line in input_str.splitlines(): + if line.startswith(b"Tag: "): + tags.append(line.split(b" ")[1].rstrip().decode("ascii")) + elif line.startswith(b"Build: "): + existing_build_number = line.split(b" ")[1].rstrip().decode("ascii") + + return tags, existing_build_number + + +def set_build_number(wheel_file_content: str, build_number: str | None) -> str: + """Compute a build tag and add/replace/remove as necessary. + + :param wheel_file_content: The contents of .dist-info/WHEEL + :param build_number: The build tags present in .dist-info/WHEEL + :return: The (modified) contents of .dist-info/WHEEL + """ + replacement = ( + ("Build: %s\r\n" % build_number).encode("ascii") if build_number else b"" + ) + + wheel_file_content, num_replaced = BUILD_NUM_RE.subn( + replacement, wheel_file_content + ) + + if not num_replaced: + wheel_file_content += replacement + + return wheel_file_content + + +def compute_tagline(tags: list[str]) -> str: + """Compute a tagline from a list of tags. + + :param tags: A list of tags + :return: A tagline + """ + impls = sorted({tag.split("-")[0] for tag in tags}) + abivers = sorted({tag.split("-")[1] for tag in tags}) + platforms = sorted({tag.split("-")[2] for tag in tags}) + return "-".join([".".join(impls), ".".join(abivers), ".".join(platforms)]) diff --git a/src/wheel/cli/tags.py b/src/wheel/cli/tags.py index ba8130b5..15b61f83 100644 --- a/src/wheel/cli/tags.py +++ b/src/wheel/cli/tags.py @@ -2,14 +2,9 @@ import itertools import os -import shutil -import sys -import tempfile -from contextlib import contextmanager from ..wheelfile import WheelFile -from .pack import pack -from .unpack import unpack +from .pack import read_tags, set_build_number try: from typing import Iterator @@ -17,59 +12,6 @@ pass -@contextmanager -def redirect_stdout(new_target): - old_target, sys.stdout = sys.stdout, new_target - try: - yield new_target - finally: - sys.stdout = old_target - - -@contextmanager -def temporary_directory(): - try: - dirname = tempfile.mkdtemp() - yield dirname - finally: - shutil.rmtree(dirname) - - -class InWheelCtx: - @property - def parsed_filename(self): - return self.wheel.parsed_filename - - @property - def filename(self): - return self.wheel.filename - - def __init__(self, wheel, tmpdir): - self.wheel = WheelFile(wheel) - self.tmpdir = tmpdir - self.build_number = None - # If dirname is unset, don't pack a new wheel - self.dirname = None - - def __enter__(self): - with redirect_stdout(sys.stderr): - unpack(self.wheel.filename, self.tmpdir) - self.wheel.__enter__() - return self - - def __exit__(self, *args): - self.wheel.__exit__(*args) - if self.dirname: - with redirect_stdout(sys.stderr): - pack( - os.path.join( - self.tmpdir, self.wheel.parsed_filename.group("namever") - ), - self.dirname, - self.build_number, - ) - - def compute_tags(original_tags: list[str], new_tags: list[str] | None) -> list[str]: """Add or replace tags.""" @@ -104,52 +46,103 @@ def tags( """ for wheel in wheels: - with temporary_directory() as tmpdir, InWheelCtx(wheel, tmpdir) as wfctx: - namever = wfctx.parsed_filename.group("namever") - build = wfctx.parsed_filename.group("build") - original_python_tags = wfctx.parsed_filename.group("pyver").split(".") - original_abi_tags = wfctx.parsed_filename.group("abi").split(".") - orignial_plat_tags = wfctx.parsed_filename.group("plat").split(".") - - if build_number is not None: - build = str(build_number) - - final_python_tags = compute_tags(original_python_tags, python_tags) - final_abi_tags = compute_tags(original_abi_tags, abi_tags) - final_plat_tags = compute_tags(orignial_plat_tags, platform_tags) - - final_tags = [ - ".".join(sorted(final_python_tags)), - ".".join(sorted(final_abi_tags)), - ".".join(sorted(final_plat_tags)), + with WheelFile(wheel, "r") as f: + 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(".") + orignial_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): + raise AssertionError(f"{impls} != {original_python_tags}") + + if abivers != set(original_abi_tags): + raise AssertionError(f"{abivers} != {original_abi_tags}") + + if platforms != set(orignial_plat_tags): + raise AssertionError(f"{platforms} != {orignial_plat_tags}") + + if existing_build_number != build: + raise AssertionError( + f"Incorrect filename '{build}' & " + f"*.dist-info/WHEEL '{existing_build_number}' build numbers" + ) + + # Start changing as needed + if build_number is not None: + build = str(build_number) + + final_python_tags = compute_tags(original_python_tags, python_tags) + final_abi_tags = compute_tags(original_abi_tags, abi_tags) + final_plat_tags = compute_tags(orignial_plat_tags, platform_tags) + + final_tags = [ + ".".join(sorted(final_python_tags)), + ".".join(sorted(final_abi_tags)), + ".".join(sorted(final_plat_tags)), + ] + + if build: + final_tags.insert(0, build) + final_tags.insert(0, namever) + + 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 + ) ] - if build: - final_tags.insert(0, build) - final_tags.insert(0, namever) + 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) + + yield final_wheel_name - original_wheel_name = os.path.basename(wfctx.filename) - final_wheel_name = "-".join(final_tags) + ".whl" - if original_wheel_name != final_wheel_name: +def set_tags(in_string: bytes, tags: list[str]) -> bytes: + """Set the tags in the .dist-info/WHEEL file contents. - wheelinfo = os.path.join( - tmpdir, namever, wfctx.wheel.dist_info_path, "WHEEL" - ) - with open(wheelinfo, "rb+") as f: - lines = [line for line in f if not line.startswith(b"Tag:")] - for a, b, c in itertools.product( - final_python_tags, final_abi_tags, final_plat_tags - ): - lines.append(f"Tag: {a}-{b}-{c}\r\n".encode("ascii")) - f.seek(0) - f.truncate() - f.write(b"".join(lines)) - - wfctx.build_number = build - wfctx.dirname = os.path.dirname(wheel) - - if remove: - os.remove(wheel) + :param in_string: The string to modify. + :param tags: The tags to set. + """ - yield final_wheel_name + 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 diff --git a/tests/cli/test_tags.py b/tests/cli/test_tags.py index bc93e426..85786111 100644 --- a/tests/cli/test_tags.py +++ b/tests/cli/test_tags.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from zipfile import ZipFile import py import pytest @@ -168,3 +169,31 @@ def test_tags_command_del(capsys, wheelpath): assert "test-1.0-py2.py3.py4-cp33m-linux_x86_64.whl" == newname output_file = wheelpath.dirpath(newname) output_file.remove() + + +def test_permission_bits(capsys, wheelpath): + args = [ + "tags", + "--python-tag", + ".py4", + str(wheelpath), + ] + p = parser() + args = p.parse_args(args) + args.func(args) + + newname = capsys.readouterr().out.strip() + assert "test-1.0-py2.py3.py4-none-any.whl" == newname + output_file = wheelpath.dirpath(newname) + + with ZipFile(str(output_file), "r") as outf: + with ZipFile(str(wheelpath), "r") as inf: + for member in inf.namelist(): + if not member.endswith("/RECORD"): + out_attr = outf.getinfo(member).external_attr + inf_attr = inf.getinfo(member).external_attr + assert ( + out_attr == inf_attr + ), f"{member} 0x{out_attr:012o} != 0x{inf_attr:012o}" + + output_file.remove() From bff25a320dbec1c8130501ab6f954c210862c1bd Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 30 Dec 2021 14:49:07 -0500 Subject: [PATCH 03/12] refactor: tags run on one wheel each (simpler impl) --- src/wheel/cli/__init__.py | 21 +++-- src/wheel/cli/tags.py | 173 ++++++++++++++++++-------------------- tests/cli/test_tags.py | 30 +++---- 3 files changed, 110 insertions(+), 114 deletions(-) diff --git a/src/wheel/cli/__init__.py b/src/wheel/cli/__init__.py index 663189c4..e3e5794b 100644 --- a/src/wheel/cli/__init__.py +++ b/src/wheel/cli/__init__.py @@ -34,14 +34,19 @@ def convert_f(args): def tags_f(args): from .tags import tags - for name in tags( - args.wheel, - args.python_tag and args.python_tag.split("."), - args.abi_tag and args.abi_tag.split("."), - args.platform_tag and args.platform_tag.split("."), - args.build, - args.remove, - ): + names = ( + tags( + wheel, + args.python_tag and args.python_tag.split("."), + args.abi_tag and args.abi_tag.split("."), + args.platform_tag and args.platform_tag.split("."), + args.build, + args.remove, + ) + for wheel in args.wheel + ) + + for name in names: print(name) diff --git a/src/wheel/cli/tags.py b/src/wheel/cli/tags.py index 15b61f83..28326b96 100644 --- a/src/wheel/cli/tags.py +++ b/src/wheel/cli/tags.py @@ -6,11 +6,6 @@ from ..wheelfile import WheelFile from .pack import read_tags, set_build_number -try: - from typing import Iterator -except ImportError: - pass - def compute_tags(original_tags: list[str], new_tags: list[str] | None) -> list[str]: """Add or replace tags.""" @@ -25,13 +20,13 @@ def compute_tags(original_tags: list[str], new_tags: list[str] | None) -> list[s def tags( - wheels: list[str], + wheel: str, python_tags: list[str] | None = None, abi_tags: list[str] | None = None, platform_tags: list[str] | None = None, build_number: int | None = None, remove: bool = False, -) -> Iterator[str]: +) -> str: """Change the tags on a wheel file. The tags are left unchanged if they are not specified. To specify "none", @@ -44,93 +39,89 @@ def tags( :param build_number: The build number to set. :param remove: Remove the original wheel. """ - - for wheel in wheels: - with WheelFile(wheel, "r") as f: - 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(".") - orignial_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): - raise AssertionError(f"{impls} != {original_python_tags}") - - if abivers != set(original_abi_tags): - raise AssertionError(f"{abivers} != {original_abi_tags}") - - if platforms != set(orignial_plat_tags): - raise AssertionError(f"{platforms} != {orignial_plat_tags}") - - if existing_build_number != build: - raise AssertionError( - f"Incorrect filename '{build}' & " - f"*.dist-info/WHEEL '{existing_build_number}' build numbers" + with WheelFile(wheel, "r") as f: + 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(".") + orignial_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): + raise AssertionError(f"{impls} != {original_python_tags}") + + if abivers != set(original_abi_tags): + raise AssertionError(f"{abivers} != {original_abi_tags}") + + if platforms != set(orignial_plat_tags): + raise AssertionError(f"{platforms} != {orignial_plat_tags}") + + if existing_build_number != build: + raise AssertionError( + f"Incorrect filename '{build}' & " + f"*.dist-info/WHEEL '{existing_build_number}' build numbers" + ) + + # Start changing as needed + if build_number is not None: + build = str(build_number) + + final_python_tags = compute_tags(original_python_tags, python_tags) + final_abi_tags = compute_tags(original_abi_tags, abi_tags) + final_plat_tags = compute_tags(orignial_plat_tags, platform_tags) + + final_tags = [ + ".".join(sorted(final_python_tags)), + ".".join(sorted(final_abi_tags)), + ".".join(sorted(final_plat_tags)), + ] + + if build: + final_tags.insert(0, build) + final_tags.insert(0, namever) + + 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 ) - - # Start changing as needed - if build_number is not None: - build = str(build_number) - - final_python_tags = compute_tags(original_python_tags, python_tags) - final_abi_tags = compute_tags(original_abi_tags, abi_tags) - final_plat_tags = compute_tags(orignial_plat_tags, platform_tags) - - final_tags = [ - ".".join(sorted(final_python_tags)), - ".".join(sorted(final_abi_tags)), - ".".join(sorted(final_plat_tags)), ] - if build: - final_tags.insert(0, build) - final_tags.insert(0, namever) - - 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) - - yield final_wheel_name + 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: list[str]) -> bytes: diff --git a/tests/cli/test_tags.py b/tests/cli/test_tags.py index 85786111..0044cd0b 100644 --- a/tests/cli/test_tags.py +++ b/tests/cli/test_tags.py @@ -23,13 +23,13 @@ def wheelpath(tmpdir): def test_tags_no_args(wheelpath): - (newname,) = tags([str(wheelpath)]) + newname = tags(str(wheelpath)) assert TESTWHEEL_NAME == newname assert wheelpath.exists() def test_python_tags(wheelpath): - (newname,) = tags([str(wheelpath)], python_tags=["py3"]) + newname = tags(str(wheelpath), python_tags=["py3"]) assert TESTWHEEL_NAME.replace("py2.py3", "py3") == newname output_file = wheelpath.dirpath(newname) with WheelFile(str(output_file)) as f: @@ -40,10 +40,10 @@ def test_python_tags(wheelpath): ) output_file.remove() - (newname,) = tags([str(wheelpath)], python_tags=["py2.py3"]) + newname = tags(str(wheelpath), python_tags=["py2.py3"]) assert TESTWHEEL_NAME == newname - (newname,) = tags([str(wheelpath)], python_tags=["", "py4"], remove=True) + newname = tags(str(wheelpath), python_tags=["", "py4"], remove=True) assert not wheelpath.exists() assert TESTWHEEL_NAME.replace("py2.py3", "py2.py3.py4") == newname output_file = wheelpath.dirpath(newname) @@ -51,20 +51,20 @@ def test_python_tags(wheelpath): def test_abi_tags(wheelpath): - (newname,) = tags([str(wheelpath)], abi_tags=["cp33m"]) + newname = tags(str(wheelpath), abi_tags=["cp33m"]) assert TESTWHEEL_NAME.replace("none", "cp33m") == newname output_file = wheelpath.dirpath(newname) output_file.remove() - (newname,) = tags([str(wheelpath)], abi_tags=["abi3", "cp33m"]) + newname = tags(str(wheelpath), abi_tags=["abi3", "cp33m"]) assert TESTWHEEL_NAME.replace("none", "abi3.cp33m") == newname output_file = wheelpath.dirpath(newname) output_file.remove() - (newname,) = tags([str(wheelpath)], abi_tags=["none"]) + newname = tags(str(wheelpath), abi_tags=["none"]) assert TESTWHEEL_NAME == newname - (newname,) = tags([str(wheelpath)], abi_tags=["", "abi3", "cp33m"], remove=True) + newname = tags(str(wheelpath), abi_tags=["", "abi3", "cp33m"], remove=True) assert not wheelpath.exists() assert TESTWHEEL_NAME.replace("none", "abi3.cp33m.none") == newname output_file = wheelpath.dirpath(newname) @@ -72,30 +72,30 @@ def test_abi_tags(wheelpath): def test_plat_tags(wheelpath): - (newname,) = tags([str(wheelpath)], platform_tags=["linux_x86_64"]) + newname = tags(str(wheelpath), platform_tags=["linux_x86_64"]) assert TESTWHEEL_NAME.replace("any", "linux_x86_64") == newname output_file = wheelpath.dirpath(newname) assert output_file.exists() output_file.remove() - (newname,) = tags([str(wheelpath)], platform_tags=["linux_x86_64", "win32"]) + newname = tags(str(wheelpath), platform_tags=["linux_x86_64", "win32"]) assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname output_file = wheelpath.dirpath(newname) assert output_file.exists() output_file.remove() - (newname,) = tags([str(wheelpath)], platform_tags=["", "linux_x86_64", "win32"]) + newname = tags(str(wheelpath), platform_tags=["", "linux_x86_64", "win32"]) assert TESTWHEEL_NAME.replace("any", "any.linux_x86_64.win32") == newname output_file = wheelpath.dirpath(newname) assert output_file.exists() output_file.remove() - (newname,) = tags([str(wheelpath)], platform_tags=["any"]) + newname = tags(str(wheelpath), platform_tags=["any"]) assert TESTWHEEL_NAME == newname def test_build_number(wheelpath): - (newname,) = tags([str(wheelpath)], build_number=1) + newname = tags(str(wheelpath), build_number=1) assert TESTWHEEL_NAME.replace("-py2", "-1-py2") == newname output_file = wheelpath.dirpath(newname) assert output_file.exists() @@ -103,8 +103,8 @@ def test_build_number(wheelpath): def test_multi_tags(wheelpath): - (newname,) = tags( - [str(wheelpath)], + newname = tags( + str(wheelpath), platform_tags=["linux_x86_64"], python_tags=["", "py4"], build_number=1, From 90a045e93ca870749768982589ed83cf76101076 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Thu, 22 Dec 2022 18:17:43 -0500 Subject: [PATCH 04/12] chore: address feedback without CLI change yet Signed-off-by: Henry Schreiner --- src/wheel/cli/tags.py | 41 ++++++++++++++----------- tests/cli/test_tags.py | 69 ++++++++++++++++++++++-------------------- 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/src/wheel/cli/tags.py b/src/wheel/cli/tags.py index 28326b96..4e1e37d2 100644 --- a/src/wheel/cli/tags.py +++ b/src/wheel/cli/tags.py @@ -32,12 +32,12 @@ def tags( The tags are left unchanged if they are not specified. To specify "none", use ["none"]. To append to the previous tags, use ["", ...]. - :param wheels: 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. + :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: wheel_info = f.read(f.dist_info_path + "/WHEEL") @@ -47,7 +47,7 @@ def tags( build = f.parsed_filename.group("build") original_python_tags = f.parsed_filename.group("pyver").split(".") original_abi_tags = f.parsed_filename.group("abi").split(".") - orignial_plat_tags = f.parsed_filename.group("plat").split(".") + original_plat_tags = f.parsed_filename.group("plat").split(".") tags, existing_build_number = read_tags(wheel_info) @@ -56,19 +56,25 @@ def tags( platforms = {tag.split("-")[2] for tag in tags} if impls != set(original_python_tags): - raise AssertionError(f"{impls} != {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): - raise AssertionError(f"{abivers} != {original_abi_tags}") + msg = f"Wheel internal tags {abivers!r} != filename tags {original_abi_tags!r}" + raise AssertionError(msg) - if platforms != set(orignial_plat_tags): - raise AssertionError(f"{platforms} != {orignial_plat_tags}") + 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: - raise AssertionError( - f"Incorrect filename '{build}' & " - f"*.dist-info/WHEEL '{existing_build_number}' build numbers" + 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: @@ -76,17 +82,16 @@ def tags( final_python_tags = compute_tags(original_python_tags, python_tags) final_abi_tags = compute_tags(original_abi_tags, abi_tags) - final_plat_tags = compute_tags(orignial_plat_tags, platform_tags) + final_plat_tags = compute_tags(original_plat_tags, platform_tags) final_tags = [ + namever, ".".join(sorted(final_python_tags)), ".".join(sorted(final_abi_tags)), ".".join(sorted(final_plat_tags)), ] - if build: - final_tags.insert(0, build) - final_tags.insert(0, namever) + final_tags.insert(1, build) final_wheel_name = "-".join(final_tags) + ".whl" diff --git a/tests/cli/test_tags.py b/tests/cli/test_tags.py index 0044cd0b..82ca8732 100644 --- a/tests/cli/test_tags.py +++ b/tests/cli/test_tags.py @@ -1,24 +1,27 @@ from __future__ import annotations -import os +import shutil +from pathlib import Path from zipfile import ZipFile -import py import pytest from wheel.cli import parser from wheel.cli.tags import tags from wheel.wheelfile import WheelFile -THISDIR = os.path.dirname(__file__) +TESTDIR = Path(__file__).parent.parent TESTWHEEL_NAME = "test-1.0-py2.py3-none-any.whl" -TESTWHEEL_PATH = os.path.join(THISDIR, "..", "testdata", TESTWHEEL_NAME) +TESTWHEEL_PATH = TESTDIR / "testdata" / TESTWHEEL_NAME @pytest.fixture -def wheelpath(tmpdir): - fn = tmpdir.mkdir("wheels").join(TESTWHEEL_NAME) - py.path.local(TESTWHEEL_PATH).copy(fn) +def wheelpath(tmp_path): + wheels_dir = tmp_path / "wheels" + wheels_dir.mkdir() + fn = wheels_dir / TESTWHEEL_NAME + # The str calls can be removed for Python 3.8+ + shutil.copy(str(TESTWHEEL_PATH), str(fn)) return fn @@ -31,14 +34,14 @@ def test_tags_no_args(wheelpath): def test_python_tags(wheelpath): newname = tags(str(wheelpath), python_tags=["py3"]) assert TESTWHEEL_NAME.replace("py2.py3", "py3") == newname - output_file = wheelpath.dirpath(newname) + output_file = wheelpath.parent / newname with WheelFile(str(output_file)) as f: output = f.read(f.dist_info_path + "/WHEEL") assert ( output == b"Wheel-Version: 1.0\r\nGenerator: bdist_wheel (0.30.0)" b"\r\nRoot-Is-Purelib: false\r\nTag: py3-none-any\r\n" ) - output_file.remove() + output_file.unlink() newname = tags(str(wheelpath), python_tags=["py2.py3"]) assert TESTWHEEL_NAME == newname @@ -46,20 +49,20 @@ def test_python_tags(wheelpath): newname = tags(str(wheelpath), python_tags=["", "py4"], remove=True) assert not wheelpath.exists() assert TESTWHEEL_NAME.replace("py2.py3", "py2.py3.py4") == newname - output_file = wheelpath.dirpath(newname) - output_file.remove() + output_file = wheelpath.parent / newname + output_file.unlink() def test_abi_tags(wheelpath): newname = tags(str(wheelpath), abi_tags=["cp33m"]) assert TESTWHEEL_NAME.replace("none", "cp33m") == newname - output_file = wheelpath.dirpath(newname) - output_file.remove() + output_file = wheelpath.parent / newname + output_file.unlink() newname = tags(str(wheelpath), abi_tags=["abi3", "cp33m"]) assert TESTWHEEL_NAME.replace("none", "abi3.cp33m") == newname - output_file = wheelpath.dirpath(newname) - output_file.remove() + output_file = wheelpath.parent / newname + output_file.unlink() newname = tags(str(wheelpath), abi_tags=["none"]) assert TESTWHEEL_NAME == newname @@ -67,28 +70,28 @@ def test_abi_tags(wheelpath): newname = tags(str(wheelpath), abi_tags=["", "abi3", "cp33m"], remove=True) assert not wheelpath.exists() assert TESTWHEEL_NAME.replace("none", "abi3.cp33m.none") == newname - output_file = wheelpath.dirpath(newname) - output_file.remove() + output_file = wheelpath.parent / newname + output_file.unlink() def test_plat_tags(wheelpath): newname = tags(str(wheelpath), platform_tags=["linux_x86_64"]) assert TESTWHEEL_NAME.replace("any", "linux_x86_64") == newname - output_file = wheelpath.dirpath(newname) + output_file = wheelpath.parent / newname assert output_file.exists() - output_file.remove() + output_file.unlink() newname = tags(str(wheelpath), platform_tags=["linux_x86_64", "win32"]) assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname - output_file = wheelpath.dirpath(newname) + output_file = wheelpath.parent / newname assert output_file.exists() - output_file.remove() + output_file.unlink() newname = tags(str(wheelpath), platform_tags=["", "linux_x86_64", "win32"]) assert TESTWHEEL_NAME.replace("any", "any.linux_x86_64.win32") == newname - output_file = wheelpath.dirpath(newname) + output_file = wheelpath.parent / newname assert output_file.exists() - output_file.remove() + output_file.unlink() newname = tags(str(wheelpath), platform_tags=["any"]) assert TESTWHEEL_NAME == newname @@ -97,9 +100,9 @@ def test_plat_tags(wheelpath): def test_build_number(wheelpath): newname = tags(str(wheelpath), build_number=1) assert TESTWHEEL_NAME.replace("-py2", "-1-py2") == newname - output_file = wheelpath.dirpath(newname) + output_file = wheelpath.parent / newname assert output_file.exists() - output_file.remove() + output_file.unlink() def test_multi_tags(wheelpath): @@ -111,7 +114,7 @@ def test_multi_tags(wheelpath): ) assert "test-1.0-1-py2.py3.py4-none-linux_x86_64.whl" == newname - output_file = wheelpath.dirpath(newname) + output_file = wheelpath.parent / newname assert output_file.exists() with WheelFile(str(output_file)) as f: output = f.read(f.dist_info_path + "/WHEEL") @@ -121,7 +124,7 @@ def test_multi_tags(wheelpath): b" false\r\nTag: py2-none-linux_x86_64\r\nTag: py3-none-linux_x86_64\r\nTag:" b" py4-none-linux_x86_64\r\nBuild: 1\r\n" ) - output_file.remove() + output_file.unlink() def test_tags_command(capsys, wheelpath): @@ -144,8 +147,8 @@ def test_tags_command(capsys, wheelpath): newname = capsys.readouterr().out.strip() assert "test-1.0-7-py3-cp33m-linux_x86_64.whl" == newname - output_file = wheelpath.dirpath(newname) - output_file.remove() + output_file = wheelpath.parent / newname + output_file.unlink() def test_tags_command_del(capsys, wheelpath): @@ -167,8 +170,8 @@ def test_tags_command_del(capsys, wheelpath): newname = capsys.readouterr().out.strip() assert "test-1.0-py2.py3.py4-cp33m-linux_x86_64.whl" == newname - output_file = wheelpath.dirpath(newname) - output_file.remove() + output_file = wheelpath.parent / newname + output_file.unlink() def test_permission_bits(capsys, wheelpath): @@ -184,7 +187,7 @@ def test_permission_bits(capsys, wheelpath): newname = capsys.readouterr().out.strip() assert "test-1.0-py2.py3.py4-none-any.whl" == newname - output_file = wheelpath.dirpath(newname) + output_file = wheelpath.parent / newname with ZipFile(str(output_file), "r") as outf: with ZipFile(str(wheelpath), "r") as inf: @@ -196,4 +199,4 @@ def test_permission_bits(capsys, wheelpath): out_attr == inf_attr ), f"{member} 0x{out_attr:012o} != 0x{inf_attr:012o}" - output_file.remove() + output_file.unlink() From 7cd6af4ed19e78b6b5a2fd3357effb138e1d133c Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Mon, 30 Jan 2023 16:29:21 -0500 Subject: [PATCH 05/12] refactor: use +/- mechanism Signed-off-by: Henry Schreiner --- src/wheel/cli/__init__.py | 20 ++++++++++--------- src/wheel/cli/tags.py | 42 ++++++++++++++++++++++++--------------- tests/cli/test_tags.py | 37 ++++++++++++++++++++++++---------- 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/wheel/cli/__init__.py b/src/wheel/cli/__init__.py index e3e5794b..6da43b95 100644 --- a/src/wheel/cli/__init__.py +++ b/src/wheel/cli/__init__.py @@ -37,9 +37,9 @@ def tags_f(args): names = ( tags( wheel, - args.python_tag and args.python_tag.split("."), - args.abi_tag and args.abi_tag.split("."), - args.platform_tag and args.platform_tag.split("."), + args.python_tag, + args.abi_tag, + args.platform_tag, args.build, args.remove, ) @@ -58,9 +58,9 @@ def version_f(args): TAGS_HELP = """\ Make a new wheel with given tags. Any tags unspecified will remain the same. -Separate multiple tags with a dot. Starting with a dot will append to the -existing tags. The original file will remain unless --remove is given. The -output file(s) will be displayed on stdout. +Starting with a "+" will append to the existing tags. Starting with a "-" will +remove a tag. The original file will remain unless --remove is given. The +output filename(s) will be displayed on stdout for further processing. """ @@ -109,11 +109,13 @@ def parser(): help="Remove the original files, keeping only the renamed ones", ) tags_parser.add_argument( - "--python-tag", metavar="TAG", help="Specify an interpreter tag(s)" + "--python-tag", metavar="TAG", nargs="+", help="Specify an interpreter tag(s)" ) - tags_parser.add_argument("--abi-tag", metavar="TAG", help="Specify an ABI tag(s)") tags_parser.add_argument( - "--platform-tag", metavar="TAG", help="Specify a platform tag(s)" + "--abi-tag", metavar="TAG", nargs="+", help="Specify an ABI tag(s)" + ) + tags_parser.add_argument( + "--platform-tag", metavar="TAG", nargs="+", help="Specify a platform tag(s)" ) tags_parser.add_argument( "--build", type=int, metavar="NUMBER", help="Specify a build number" diff --git a/src/wheel/cli/tags.py b/src/wheel/cli/tags.py index 4e1e37d2..d29bb9e2 100644 --- a/src/wheel/cli/tags.py +++ b/src/wheel/cli/tags.py @@ -1,36 +1,46 @@ from __future__ import annotations +import contextlib 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: list[str], new_tags: list[str] | None) -> list[str]: - """Add or replace tags.""" +def _compute_tags(original_tags: Iterable[str], new_tags: Iterable[str]) -> list[str]: + """Add or replace tags. Supports dot-separated tags""" - if not new_tags: - return original_tags + return_tags = list(original_tags) - if new_tags[0] == "": - return original_tags + new_tags[1:] - else: - return new_tags + for tag in new_tags: + if tag.startswith("+"): + return_tags.extend(tag[1:].split(".")) + elif tag.startswith("-"): + for t in tag[1:].split("."): + with contextlib.suppress(ValueError): + return_tags.remove(t) + else: + return_tags = tag.split(".") + + return return_tags def tags( wheel: str, - python_tags: list[str] | None = None, - abi_tags: list[str] | None = None, - platform_tags: list[str] | None = None, + python_tags: Iterable[str] | None = None, + abi_tags: Iterable[str] | None = None, + platform_tags: Iterable[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, use ["", ...]. + 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 @@ -80,9 +90,9 @@ def tags( if build_number is not None: build = str(build_number) - final_python_tags = compute_tags(original_python_tags, python_tags) - final_abi_tags = compute_tags(original_abi_tags, abi_tags) - final_plat_tags = compute_tags(original_plat_tags, platform_tags) + final_python_tags = _compute_tags(original_python_tags, python_tags or []) + final_abi_tags = _compute_tags(original_abi_tags, abi_tags or []) + final_plat_tags = _compute_tags(original_plat_tags, platform_tags or []) final_tags = [ namever, @@ -129,7 +139,7 @@ def tags( return final_wheel_name -def set_tags(in_string: bytes, tags: list[str]) -> bytes: +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. diff --git a/tests/cli/test_tags.py b/tests/cli/test_tags.py index 82ca8732..e9f862eb 100644 --- a/tests/cli/test_tags.py +++ b/tests/cli/test_tags.py @@ -43,10 +43,10 @@ def test_python_tags(wheelpath): ) output_file.unlink() - newname = tags(str(wheelpath), python_tags=["py2.py3"]) + newname = tags(str(wheelpath), python_tags=["py2", "+py3"]) assert TESTWHEEL_NAME == newname - newname = tags(str(wheelpath), python_tags=["", "py4"], remove=True) + newname = tags(str(wheelpath), python_tags=["+py4"], remove=True) assert not wheelpath.exists() assert TESTWHEEL_NAME.replace("py2.py3", "py2.py3.py4") == newname output_file = wheelpath.parent / newname @@ -59,7 +59,7 @@ def test_abi_tags(wheelpath): output_file = wheelpath.parent / newname output_file.unlink() - newname = tags(str(wheelpath), abi_tags=["abi3", "cp33m"]) + newname = tags(str(wheelpath), abi_tags=["abi3", "+cp33m"]) assert TESTWHEEL_NAME.replace("none", "abi3.cp33m") == newname output_file = wheelpath.parent / newname output_file.unlink() @@ -67,7 +67,7 @@ def test_abi_tags(wheelpath): newname = tags(str(wheelpath), abi_tags=["none"]) assert TESTWHEEL_NAME == newname - newname = tags(str(wheelpath), abi_tags=["", "abi3", "cp33m"], remove=True) + newname = tags(str(wheelpath), abi_tags=["+abi3", "+cp33m"], remove=True) assert not wheelpath.exists() assert TESTWHEEL_NAME.replace("none", "abi3.cp33m.none") == newname output_file = wheelpath.parent / newname @@ -81,18 +81,36 @@ def test_plat_tags(wheelpath): assert output_file.exists() output_file.unlink() - newname = tags(str(wheelpath), platform_tags=["linux_x86_64", "win32"]) + newname = tags(str(wheelpath), platform_tags=["linux_x86_64", "+win32"]) assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname output_file = wheelpath.parent / newname assert output_file.exists() output_file.unlink() - newname = tags(str(wheelpath), platform_tags=["", "linux_x86_64", "win32"]) + newname = tags(str(wheelpath), platform_tags=["linux_x86_64.win32"]) + assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname + output_file = wheelpath.parent / newname + assert output_file.exists() + output_file.unlink() + + newname = tags(str(wheelpath), platform_tags=["+linux_x86_64", "+win32"]) + assert TESTWHEEL_NAME.replace("any", "any.linux_x86_64.win32") == newname + output_file = wheelpath.parent / newname + assert output_file.exists() + output_file.unlink() + + newname = tags(str(wheelpath), platform_tags=["+linux_x86_64.win32"]) assert TESTWHEEL_NAME.replace("any", "any.linux_x86_64.win32") == newname output_file = wheelpath.parent / newname assert output_file.exists() output_file.unlink() + newname = tags(str(wheelpath), platform_tags=["-any", "+linux_x86_64.win32"]) + assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname + output_file = wheelpath.parent / newname + assert output_file.exists() + output_file.unlink() + newname = tags(str(wheelpath), platform_tags=["any"]) assert TESTWHEEL_NAME == newname @@ -109,7 +127,7 @@ def test_multi_tags(wheelpath): newname = tags( str(wheelpath), platform_tags=["linux_x86_64"], - python_tags=["", "py4"], + python_tags=["+py4"], build_number=1, ) assert "test-1.0-1-py2.py3.py4-none-linux_x86_64.whl" == newname @@ -155,7 +173,7 @@ def test_tags_command_del(capsys, wheelpath): args = [ "tags", "--python-tag", - ".py4", + "+py4", "--abi-tag", "cp33m", "--platform-tag", @@ -177,8 +195,7 @@ def test_tags_command_del(capsys, wheelpath): def test_permission_bits(capsys, wheelpath): args = [ "tags", - "--python-tag", - ".py4", + "--python-tag=+py4", str(wheelpath), ] p = parser() From 903f3635c53838ce4d2d6db3868684f8e688613a Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 31 Jan 2023 10:49:25 -0500 Subject: [PATCH 06/12] refactor: using single tag option Signed-off-by: Henry Schreiner --- .pre-commit-config.yaml | 9 ++++++++ src/wheel/cli/__init__.py | 8 +++---- src/wheel/cli/pack.py | 2 +- src/wheel/cli/tags.py | 41 +++++++++++++++++------------------ tests/cli/test_tags.py | 45 +++++++++++++++++---------------------- 5 files changed, 52 insertions(+), 53 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 22244554..d0f680da 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,6 +28,15 @@ repos: hooks: - id: black +- repo: https://github.com/pre-commit/mirrors-mypy + rev: "v0.991" + hooks: + - id: mypy + files: src + args: [] + additional_dependencies: [types-setuptools] + stages: [manual] + - repo: https://github.com/codespell-project/codespell rev: v2.2.2 hooks: diff --git a/src/wheel/cli/__init__.py b/src/wheel/cli/__init__.py index 6da43b95..1b43d468 100644 --- a/src/wheel/cli/__init__.py +++ b/src/wheel/cli/__init__.py @@ -109,13 +109,11 @@ def parser(): help="Remove the original files, keeping only the renamed ones", ) tags_parser.add_argument( - "--python-tag", metavar="TAG", nargs="+", help="Specify an interpreter tag(s)" + "--python-tag", metavar="TAG", help="Specify an interpreter tag(s)" ) + tags_parser.add_argument("--abi-tag", metavar="TAG", help="Specify an ABI tag(s)") tags_parser.add_argument( - "--abi-tag", metavar="TAG", nargs="+", help="Specify an ABI tag(s)" - ) - tags_parser.add_argument( - "--platform-tag", metavar="TAG", nargs="+", help="Specify a platform tag(s)" + "--platform-tag", metavar="TAG", help="Specify a platform tag(s)" ) tags_parser.add_argument( "--build", type=int, metavar="NUMBER", help="Specify a build number" diff --git a/src/wheel/cli/pack.py b/src/wheel/cli/pack.py index 5d797918..7c75c63f 100644 --- a/src/wheel/cli/pack.py +++ b/src/wheel/cli/pack.py @@ -91,7 +91,7 @@ def read_tags(input_str: bytes) -> tuple[list[str], str | None]: return tags, existing_build_number -def set_build_number(wheel_file_content: str, build_number: str | None) -> str: +def set_build_number(wheel_file_content: bytes, build_number: str | None) -> bytes: """Compute a build tag and add/replace/remove as necessary. :param wheel_file_content: The contents of .dist-info/WHEEL diff --git a/src/wheel/cli/tags.py b/src/wheel/cli/tags.py index d29bb9e2..0ea0f447 100644 --- a/src/wheel/cli/tags.py +++ b/src/wheel/cli/tags.py @@ -1,6 +1,5 @@ from __future__ import annotations -import contextlib import itertools import os from collections.abc import Iterable @@ -9,29 +8,25 @@ from .pack import read_tags, set_build_number -def _compute_tags(original_tags: Iterable[str], new_tags: Iterable[str]) -> list[str]: +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) - return_tags = list(original_tags) + if new_tags.startswith("+"): + return {*original_tags, *new_tags[1:].split(".")} - for tag in new_tags: - if tag.startswith("+"): - return_tags.extend(tag[1:].split(".")) - elif tag.startswith("-"): - for t in tag[1:].split("."): - with contextlib.suppress(ValueError): - return_tags.remove(t) - else: - return_tags = tag.split(".") + if new_tags.startswith("-"): + return set(original_tags) - set(new_tags[1:].split(".")) - return return_tags + return set(new_tags.split(".")) def tags( wheel: str, - python_tags: Iterable[str] | None = None, - abi_tags: Iterable[str] | None = None, - platform_tags: Iterable[str] | None = None, + python_tags: str | None = None, + abi_tags: str | None = None, + platform_tags: str | None = None, build_number: int | None = None, remove: bool = False, ) -> str: @@ -50,6 +45,8 @@ def tags( :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) @@ -90,15 +87,15 @@ def tags( if build_number is not None: build = str(build_number) - final_python_tags = _compute_tags(original_python_tags, python_tags or []) - final_abi_tags = _compute_tags(original_abi_tags, abi_tags or []) - final_plat_tags = _compute_tags(original_plat_tags, platform_tags or []) + 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(sorted(final_python_tags)), - ".".join(sorted(final_abi_tags)), - ".".join(sorted(final_plat_tags)), + ".".join(final_python_tags), + ".".join(final_abi_tags), + ".".join(final_plat_tags), ] if build: final_tags.insert(1, build) diff --git a/tests/cli/test_tags.py b/tests/cli/test_tags.py index e9f862eb..630c903f 100644 --- a/tests/cli/test_tags.py +++ b/tests/cli/test_tags.py @@ -32,7 +32,7 @@ def test_tags_no_args(wheelpath): def test_python_tags(wheelpath): - newname = tags(str(wheelpath), python_tags=["py3"]) + newname = tags(str(wheelpath), python_tags="py3") assert TESTWHEEL_NAME.replace("py2.py3", "py3") == newname output_file = wheelpath.parent / newname with WheelFile(str(output_file)) as f: @@ -43,10 +43,10 @@ def test_python_tags(wheelpath): ) output_file.unlink() - newname = tags(str(wheelpath), python_tags=["py2", "+py3"]) + newname = tags(str(wheelpath), python_tags="py2.py3") assert TESTWHEEL_NAME == newname - newname = tags(str(wheelpath), python_tags=["+py4"], remove=True) + newname = tags(str(wheelpath), python_tags="+py4", remove=True) assert not wheelpath.exists() assert TESTWHEEL_NAME.replace("py2.py3", "py2.py3.py4") == newname output_file = wheelpath.parent / newname @@ -54,20 +54,20 @@ def test_python_tags(wheelpath): def test_abi_tags(wheelpath): - newname = tags(str(wheelpath), abi_tags=["cp33m"]) + newname = tags(str(wheelpath), abi_tags="cp33m") assert TESTWHEEL_NAME.replace("none", "cp33m") == newname output_file = wheelpath.parent / newname output_file.unlink() - newname = tags(str(wheelpath), abi_tags=["abi3", "+cp33m"]) + newname = tags(str(wheelpath), abi_tags="cp33m.abi3") assert TESTWHEEL_NAME.replace("none", "abi3.cp33m") == newname output_file = wheelpath.parent / newname output_file.unlink() - newname = tags(str(wheelpath), abi_tags=["none"]) + newname = tags(str(wheelpath), abi_tags="none") assert TESTWHEEL_NAME == newname - newname = tags(str(wheelpath), abi_tags=["+abi3", "+cp33m"], remove=True) + newname = tags(str(wheelpath), abi_tags="+abi3.cp33m", remove=True) assert not wheelpath.exists() assert TESTWHEEL_NAME.replace("none", "abi3.cp33m.none") == newname output_file = wheelpath.parent / newname @@ -75,43 +75,38 @@ def test_abi_tags(wheelpath): def test_plat_tags(wheelpath): - newname = tags(str(wheelpath), platform_tags=["linux_x86_64"]) + newname = tags(str(wheelpath), platform_tags="linux_x86_64") assert TESTWHEEL_NAME.replace("any", "linux_x86_64") == newname output_file = wheelpath.parent / newname assert output_file.exists() output_file.unlink() - newname = tags(str(wheelpath), platform_tags=["linux_x86_64", "+win32"]) + newname = tags(str(wheelpath), platform_tags="linux_x86_64.win32") assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname output_file = wheelpath.parent / newname assert output_file.exists() output_file.unlink() - newname = tags(str(wheelpath), platform_tags=["linux_x86_64.win32"]) - assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname - output_file = wheelpath.parent / newname - assert output_file.exists() - output_file.unlink() - - newname = tags(str(wheelpath), platform_tags=["+linux_x86_64", "+win32"]) + newname = tags(str(wheelpath), platform_tags="+linux_x86_64.win32") assert TESTWHEEL_NAME.replace("any", "any.linux_x86_64.win32") == newname output_file = wheelpath.parent / newname assert output_file.exists() output_file.unlink() - newname = tags(str(wheelpath), platform_tags=["+linux_x86_64.win32"]) + newname = tags(str(wheelpath), platform_tags="+linux_x86_64.win32") assert TESTWHEEL_NAME.replace("any", "any.linux_x86_64.win32") == newname output_file = wheelpath.parent / newname assert output_file.exists() - output_file.unlink() - newname = tags(str(wheelpath), platform_tags=["-any", "+linux_x86_64.win32"]) - assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname - output_file = wheelpath.parent / newname - assert output_file.exists() + newname2 = tags(str(output_file), platform_tags="-any") output_file.unlink() - newname = tags(str(wheelpath), platform_tags=["any"]) + assert TESTWHEEL_NAME.replace("any", "linux_x86_64.win32") == newname2 + output_file2 = wheelpath.parent / newname2 + assert output_file2.exists() + output_file2.unlink() + + newname = tags(str(wheelpath), platform_tags="any") assert TESTWHEEL_NAME == newname @@ -126,8 +121,8 @@ def test_build_number(wheelpath): def test_multi_tags(wheelpath): newname = tags( str(wheelpath), - platform_tags=["linux_x86_64"], - python_tags=["+py4"], + platform_tags="linux_x86_64", + python_tags="+py4", build_number=1, ) assert "test-1.0-1-py2.py3.py4-none-linux_x86_64.whl" == newname From 968690aba357167b3ed99e595499fc5ba37530e2 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 31 Jan 2023 11:38:47 -0500 Subject: [PATCH 07/12] docs: adding docs for wheel tags Signed-off-by: Henry Schreiner --- docs/reference/index.rst | 1 + docs/reference/wheel_tags.rst | 60 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 docs/reference/wheel_tags.rst diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 19213236..f332026d 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -7,3 +7,4 @@ Reference Guide wheel_convert wheel_unpack wheel_pack + wheel_tags diff --git a/docs/reference/wheel_tags.rst b/docs/reference/wheel_tags.rst new file mode 100644 index 00000000..d290e691 --- /dev/null +++ b/docs/reference/wheel_tags.rst @@ -0,0 +1,60 @@ +wheel tags +========== + +Usage +----- + +:: + + wheel tagstags [-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. 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 From a4725c24581ce99096aad8f3d95f233b6ac781e0 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 31 Jan 2023 12:17:48 -0500 Subject: [PATCH 08/12] fixup! docs: adding docs for wheel tags --- docs/reference/wheel_tags.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/reference/wheel_tags.rst b/docs/reference/wheel_tags.rst index d290e691..c654709e 100644 --- a/docs/reference/wheel_tags.rst +++ b/docs/reference/wheel_tags.rst @@ -25,22 +25,22 @@ Options Remove the original wheel, keeping only the retagged wheel. -.. option:: --python-tag TAG +.. 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 +.. 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 +.. 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 +.. option:: --build=NUMBER Specify a build number. @@ -49,12 +49,12 @@ 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 + $ 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 \ + --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 From 793358fb879fbb95a8f3cda926c59cc0a1200bcc Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 1 Feb 2023 10:10:10 -0500 Subject: [PATCH 09/12] fixup! docs: adding docs for wheel tags --- docs/reference/wheel_tags.rst | 6 ++++-- src/wheel/cli/__init__.py | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/reference/wheel_tags.rst b/docs/reference/wheel_tags.rst index c654709e..7b4e5051 100644 --- a/docs/reference/wheel_tags.rst +++ b/docs/reference/wheel_tags.rst @@ -14,8 +14,10 @@ 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. The original file will remain unless ``--remove`` is given. The output -filename(s) will be displayed on stdout for further processing. +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 diff --git a/src/wheel/cli/__init__.py b/src/wheel/cli/__init__.py index 1b43d468..fa1f10bc 100644 --- a/src/wheel/cli/__init__.py +++ b/src/wheel/cli/__init__.py @@ -58,8 +58,9 @@ def version_f(args): TAGS_HELP = """\ Make a new wheel with given tags. Any tags unspecified will remain the same. -Starting with a "+" will append to the existing tags. Starting with a "-" will -remove a tag. The original file will remain unless --remove is given. The +Starting the tags with a "+" will append to the existing tags. Starting with a +"-" will remove a tag (use --option=-TAG syntax). Multiple tags can be +separated by ".". The original file will remain unless --remove is given. The output filename(s) will be displayed on stdout for further processing. """ From 94ec75e53823231de6798c0015c60fbd99be552f Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 17 Feb 2023 08:59:55 -0500 Subject: [PATCH 10/12] Update docs/reference/wheel_tags.rst Co-authored-by: Joe Rickerby --- docs/reference/wheel_tags.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/wheel_tags.rst b/docs/reference/wheel_tags.rst index 7b4e5051..f4370710 100644 --- a/docs/reference/wheel_tags.rst +++ b/docs/reference/wheel_tags.rst @@ -6,7 +6,7 @@ Usage :: - wheel tagstags [-h] [--remove] [--python-tag TAG] [--abi-tag TAG] [--platform-tag TAG] [--build NUMBER] WHEEL [...] + wheel tags [-h] [--remove] [--python-tag TAG] [--abi-tag TAG] [--platform-tag TAG] [--build NUMBER] WHEEL [...] Description ----------- From 5b9dd227fff14865951bf54a2fb6e33c76589cad Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 10 Mar 2023 16:16:44 -0500 Subject: [PATCH 11/12] chore: adjust for pyproject.toml backend Signed-off-by: Henry Schreiner --- .pre-commit-config.yaml | 2 +- pyproject.toml | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d0f680da..12ff1a6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - id: black - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v0.991" + rev: "v1.1.1" hooks: - id: mypy files: src diff --git a/pyproject.toml b/pyproject.toml index 13715634..c40cb191 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,6 +98,13 @@ ignore = [ target-version = "py37" src = ["src"] +[tool.mypy] +files = ["src"] + +[[tool.mypy.overrides]] +module = ["_manylinux"] +ignore_missing_imports = true + [tool.tox] legacy_tox_ini = ''' # Tox (http://tox.testrun.org/) is a tool for running tests From af574fb19dacaeeebaa41c432a1e6ac0ec87953b Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sat, 11 Mar 2023 14:32:30 -0500 Subject: [PATCH 12/12] chore: remove manual mypy Signed-off-by: Henry Schreiner --- .pre-commit-config.yaml | 9 --------- pyproject.toml | 7 ------- 2 files changed, 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 12ff1a6e..22244554 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,15 +28,6 @@ repos: hooks: - id: black -- repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.1.1" - hooks: - - id: mypy - files: src - args: [] - additional_dependencies: [types-setuptools] - stages: [manual] - - repo: https://github.com/codespell-project/codespell rev: v2.2.2 hooks: diff --git a/pyproject.toml b/pyproject.toml index c40cb191..13715634 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,13 +98,6 @@ ignore = [ target-version = "py37" src = ["src"] -[tool.mypy] -files = ["src"] - -[[tool.mypy.overrides]] -module = ["_manylinux"] -ignore_missing_imports = true - [tool.tox] legacy_tox_ini = ''' # Tox (http://tox.testrun.org/) is a tool for running tests