aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli')
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/__init__.py155
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/convert.py332
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/pack.py85
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/tags.py139
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/unpack.py30
5 files changed, 741 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/__init__.py
new file mode 100644
index 00000000..6ba1217f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/__init__.py
@@ -0,0 +1,155 @@
+"""
+Wheel command-line utility.
+"""
+
+from __future__ import annotations
+
+import argparse
+import os
+import sys
+from argparse import ArgumentTypeError
+
+
+class WheelError(Exception):
+ pass
+
+
+def unpack_f(args: argparse.Namespace) -> None:
+ from .unpack import unpack
+
+ unpack(args.wheelfile, args.dest)
+
+
+def pack_f(args: argparse.Namespace) -> None:
+ from .pack import pack
+
+ pack(args.directory, args.dest_dir, args.build_number)
+
+
+def convert_f(args: argparse.Namespace) -> None:
+ from .convert import convert
+
+ convert(args.files, args.dest_dir, args.verbose)
+
+
+def tags_f(args: argparse.Namespace) -> None:
+ from .tags import tags
+
+ names = (
+ tags(
+ wheel,
+ args.python_tag,
+ args.abi_tag,
+ args.platform_tag,
+ args.build,
+ args.remove,
+ )
+ for wheel in args.wheel
+ )
+
+ for name in names:
+ print(name)
+
+
+def version_f(args: argparse.Namespace) -> None:
+ from .. import __version__
+
+ print(f"wheel {__version__}")
+
+
+def parse_build_tag(build_tag: str) -> str:
+ if build_tag and not build_tag[0].isdigit():
+ raise ArgumentTypeError("build tag must begin with a digit")
+ elif "-" in build_tag:
+ raise ArgumentTypeError("invalid character ('-') in build tag")
+
+ return build_tag
+
+
+TAGS_HELP = """\
+Make a new wheel with given tags. Any tags unspecified will remain the same.
+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.
+"""
+
+
+def parser():
+ p = argparse.ArgumentParser()
+ s = p.add_subparsers(help="commands")
+
+ unpack_parser = s.add_parser("unpack", help="Unpack wheel")
+ unpack_parser.add_argument(
+ "--dest", "-d", help="Destination directory", default="."
+ )
+ unpack_parser.add_argument("wheelfile", help="Wheel file")
+ unpack_parser.set_defaults(func=unpack_f)
+
+ repack_parser = s.add_parser("pack", help="Repack wheel")
+ repack_parser.add_argument("directory", help="Root directory of the unpacked wheel")
+ repack_parser.add_argument(
+ "--dest-dir",
+ "-d",
+ default=os.path.curdir,
+ help="Directory to store the wheel (default %(default)s)",
+ )
+ repack_parser.add_argument(
+ "--build-number", help="Build tag to use in the wheel name"
+ )
+ repack_parser.set_defaults(func=pack_f)
+
+ convert_parser = s.add_parser("convert", help="Convert egg or wininst to wheel")
+ convert_parser.add_argument("files", nargs="*", help="Files to convert")
+ convert_parser.add_argument(
+ "--dest-dir",
+ "-d",
+ default=os.path.curdir,
+ help="Directory to store wheels (default %(default)s)",
+ )
+ 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=parse_build_tag, metavar="BUILD", help="Specify a build tag"
+ )
+ 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)
+
+ help_parser = s.add_parser("help", help="Show this help")
+ help_parser.set_defaults(func=lambda args: p.print_help())
+
+ return p
+
+
+def main():
+ p = parser()
+ args = p.parse_args()
+ if not hasattr(args, "func"):
+ p.print_help()
+ else:
+ try:
+ args.func(args)
+ return 0
+ except WheelError as e:
+ print(e, file=sys.stderr)
+
+ return 1
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/convert.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/convert.py
new file mode 100644
index 00000000..61d4775c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/convert.py
@@ -0,0 +1,332 @@
+from __future__ import annotations
+
+import os.path
+import re
+from abc import ABCMeta, abstractmethod
+from collections import defaultdict
+from collections.abc import Iterator
+from email.message import Message
+from email.parser import Parser
+from email.policy import EmailPolicy
+from glob import iglob
+from pathlib import Path
+from textwrap import dedent
+from zipfile import ZipFile
+
+from .. import __version__
+from ..metadata import generate_requirements
+from ..vendored.packaging.tags import parse_tag
+from ..wheelfile import WheelFile
+
+egg_filename_re = re.compile(
+ r"""
+ (?P<name>.+?)-(?P<ver>.+?)
+ (-(?P<pyver>py\d\.\d+)
+ (-(?P<arch>.+?))?
+ )?.egg$""",
+ re.VERBOSE,
+)
+egg_info_re = re.compile(
+ r"""
+ ^(?P<name>.+?)-(?P<ver>.+?)
+ (-(?P<pyver>py\d\.\d+)
+ )?.egg-info/""",
+ re.VERBOSE,
+)
+wininst_re = re.compile(
+ r"\.(?P<platform>win32|win-amd64)(?:-(?P<pyver>py\d\.\d))?\.exe$"
+)
+pyd_re = re.compile(r"\.(?P<abi>[a-z0-9]+)-(?P<platform>win32|win_amd64)\.pyd$")
+serialization_policy = EmailPolicy(
+ utf8=True,
+ mangle_from_=False,
+ max_line_length=0,
+)
+GENERATOR = f"wheel {__version__}"
+
+
+def convert_requires(requires: str, metadata: Message) -> None:
+ extra: str | None = None
+ requirements: dict[str | None, list[str]] = defaultdict(list)
+ for line in requires.splitlines():
+ line = line.strip()
+ if not line:
+ continue
+
+ if line.startswith("[") and line.endswith("]"):
+ extra = line[1:-1]
+ continue
+
+ requirements[extra].append(line)
+
+ for key, value in generate_requirements(requirements):
+ metadata.add_header(key, value)
+
+
+def convert_pkg_info(pkginfo: str, metadata: Message):
+ parsed_message = Parser().parsestr(pkginfo)
+ for key, value in parsed_message.items():
+ key_lower = key.lower()
+ if value == "UNKNOWN":
+ continue
+
+ if key_lower == "description":
+ description_lines = value.splitlines()
+ value = "\n".join(
+ (
+ description_lines[0].lstrip(),
+ dedent("\n".join(description_lines[1:])),
+ "\n",
+ )
+ )
+ metadata.set_payload(value)
+ elif key_lower == "home-page":
+ metadata.add_header("Project-URL", f"Homepage, {value}")
+ elif key_lower == "download-url":
+ metadata.add_header("Project-URL", f"Download, {value}")
+ else:
+ metadata.add_header(key, value)
+
+ metadata.replace_header("Metadata-Version", "2.4")
+
+
+def normalize(name: str) -> str:
+ return re.sub(r"[-_.]+", "-", name).lower().replace("-", "_")
+
+
+class ConvertSource(metaclass=ABCMeta):
+ name: str
+ version: str
+ pyver: str = "py2.py3"
+ abi: str = "none"
+ platform: str = "any"
+ metadata: Message
+
+ @property
+ def dist_info_dir(self) -> str:
+ return f"{self.name}-{self.version}.dist-info"
+
+ @abstractmethod
+ def generate_contents(self) -> Iterator[tuple[str, bytes]]:
+ pass
+
+
+class EggFileSource(ConvertSource):
+ def __init__(self, path: Path):
+ if not (match := egg_filename_re.match(path.name)):
+ raise ValueError(f"Invalid egg file name: {path.name}")
+
+ # Binary wheels are assumed to be for CPython
+ self.path = path
+ self.name = normalize(match.group("name"))
+ self.version = match.group("ver")
+ if pyver := match.group("pyver"):
+ self.pyver = pyver.replace(".", "")
+ if arch := match.group("arch"):
+ self.abi = self.pyver.replace("py", "cp")
+ self.platform = normalize(arch)
+
+ self.metadata = Message()
+
+ def generate_contents(self) -> Iterator[tuple[str, bytes]]:
+ with ZipFile(self.path, "r") as zip_file:
+ for filename in sorted(zip_file.namelist()):
+ # Skip pure directory entries
+ if filename.endswith("/"):
+ continue
+
+ # Handle files in the egg-info directory specially, selectively moving
+ # them to the dist-info directory while converting as needed
+ if filename.startswith("EGG-INFO/"):
+ if filename == "EGG-INFO/requires.txt":
+ requires = zip_file.read(filename).decode("utf-8")
+ convert_requires(requires, self.metadata)
+ elif filename == "EGG-INFO/PKG-INFO":
+ pkginfo = zip_file.read(filename).decode("utf-8")
+ convert_pkg_info(pkginfo, self.metadata)
+ elif filename == "EGG-INFO/entry_points.txt":
+ yield (
+ f"{self.dist_info_dir}/entry_points.txt",
+ zip_file.read(filename),
+ )
+
+ continue
+
+ # For any other file, just pass it through
+ yield filename, zip_file.read(filename)
+
+
+class EggDirectorySource(EggFileSource):
+ def generate_contents(self) -> Iterator[tuple[str, bytes]]:
+ for dirpath, _, filenames in os.walk(self.path):
+ for filename in sorted(filenames):
+ path = Path(dirpath, filename)
+ if path.parent.name == "EGG-INFO":
+ if path.name == "requires.txt":
+ requires = path.read_text("utf-8")
+ convert_requires(requires, self.metadata)
+ elif path.name == "PKG-INFO":
+ pkginfo = path.read_text("utf-8")
+ convert_pkg_info(pkginfo, self.metadata)
+ if name := self.metadata.get("Name"):
+ self.name = normalize(name)
+
+ if version := self.metadata.get("Version"):
+ self.version = version
+ elif path.name == "entry_points.txt":
+ yield (
+ f"{self.dist_info_dir}/entry_points.txt",
+ path.read_bytes(),
+ )
+
+ continue
+
+ # For any other file, just pass it through
+ yield str(path.relative_to(self.path)), path.read_bytes()
+
+
+class WininstFileSource(ConvertSource):
+ """
+ Handles distributions created with ``bdist_wininst``.
+
+ The egginfo filename has the format::
+
+ name-ver(-pyver)(-arch).egg-info
+
+ The installer filename has the format::
+
+ name-ver.arch(-pyver).exe
+
+ Some things to note:
+
+ 1. The installer filename is not definitive. An installer can be renamed
+ and work perfectly well as an installer. So more reliable data should
+ be used whenever possible.
+ 2. The egg-info data should be preferred for the name and version, because
+ these come straight from the distutils metadata, and are mandatory.
+ 3. The pyver from the egg-info data should be ignored, as it is
+ constructed from the version of Python used to build the installer,
+ which is irrelevant - the installer filename is correct here (even to
+ the point that when it's not there, any version is implied).
+ 4. The architecture must be taken from the installer filename, as it is
+ not included in the egg-info data.
+ 5. Architecture-neutral installers still have an architecture because the
+ installer format itself (being executable) is architecture-specific. We
+ should therefore ignore the architecture if the content is pure-python.
+ """
+
+ def __init__(self, path: Path):
+ self.path = path
+ self.metadata = Message()
+
+ # Determine the initial architecture and Python version from the file name
+ # (if possible)
+ if match := wininst_re.search(path.name):
+ self.platform = normalize(match.group("platform"))
+ if pyver := match.group("pyver"):
+ self.pyver = pyver.replace(".", "")
+
+ # Look for an .egg-info directory and any .pyd files for more precise info
+ egg_info_found = pyd_found = False
+ with ZipFile(self.path) as zip_file:
+ for filename in zip_file.namelist():
+ prefix, filename = filename.split("/", 1)
+ if not egg_info_found and (match := egg_info_re.match(filename)):
+ egg_info_found = True
+ self.name = normalize(match.group("name"))
+ self.version = match.group("ver")
+ if pyver := match.group("pyver"):
+ self.pyver = pyver.replace(".", "")
+ elif not pyd_found and (match := pyd_re.search(filename)):
+ pyd_found = True
+ self.abi = match.group("abi")
+ self.platform = match.group("platform")
+
+ if egg_info_found and pyd_found:
+ break
+
+ def generate_contents(self) -> Iterator[tuple[str, bytes]]:
+ dist_info_dir = f"{self.name}-{self.version}.dist-info"
+ data_dir = f"{self.name}-{self.version}.data"
+ with ZipFile(self.path, "r") as zip_file:
+ for filename in sorted(zip_file.namelist()):
+ # Skip pure directory entries
+ if filename.endswith("/"):
+ continue
+
+ # Handle files in the egg-info directory specially, selectively moving
+ # them to the dist-info directory while converting as needed
+ prefix, target_filename = filename.split("/", 1)
+ if egg_info_re.search(target_filename):
+ basename = target_filename.rsplit("/", 1)[-1]
+ if basename == "requires.txt":
+ requires = zip_file.read(filename).decode("utf-8")
+ convert_requires(requires, self.metadata)
+ elif basename == "PKG-INFO":
+ pkginfo = zip_file.read(filename).decode("utf-8")
+ convert_pkg_info(pkginfo, self.metadata)
+ elif basename == "entry_points.txt":
+ yield (
+ f"{dist_info_dir}/entry_points.txt",
+ zip_file.read(filename),
+ )
+
+ continue
+ elif prefix == "SCRIPTS":
+ target_filename = f"{data_dir}/scripts/{target_filename}"
+
+ # For any other file, just pass it through
+ yield target_filename, zip_file.read(filename)
+
+
+def convert(files: list[str], dest_dir: str, verbose: bool) -> None:
+ for pat in files:
+ for archive in iglob(pat):
+ path = Path(archive)
+ if path.suffix == ".egg":
+ if path.is_dir():
+ source: ConvertSource = EggDirectorySource(path)
+ else:
+ source = EggFileSource(path)
+ else:
+ source = WininstFileSource(path)
+
+ if verbose:
+ print(f"{archive}...", flush=True, end="")
+
+ dest_path = Path(dest_dir) / (
+ f"{source.name}-{source.version}-{source.pyver}-{source.abi}"
+ f"-{source.platform}.whl"
+ )
+ with WheelFile(dest_path, "w") as wheelfile:
+ for name_or_zinfo, contents in source.generate_contents():
+ wheelfile.writestr(name_or_zinfo, contents)
+
+ # Write the METADATA file
+ wheelfile.writestr(
+ f"{source.dist_info_dir}/METADATA",
+ source.metadata.as_string(policy=serialization_policy).encode(
+ "utf-8"
+ ),
+ )
+
+ # Write the WHEEL file
+ wheel_message = Message()
+ wheel_message.add_header("Wheel-Version", "1.0")
+ wheel_message.add_header("Generator", GENERATOR)
+ wheel_message.add_header(
+ "Root-Is-Purelib", str(source.platform == "any").lower()
+ )
+ tags = parse_tag(f"{source.pyver}-{source.abi}-{source.platform}")
+ for tag in sorted(tags, key=lambda tag: tag.interpreter):
+ wheel_message.add_header("Tag", str(tag))
+
+ wheelfile.writestr(
+ f"{source.dist_info_dir}/WHEEL",
+ wheel_message.as_string(policy=serialization_policy).encode(
+ "utf-8"
+ ),
+ )
+
+ if verbose:
+ print("OK")
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/pack.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/pack.py
new file mode 100644
index 00000000..64469c0c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/pack.py
@@ -0,0 +1,85 @@
+from __future__ import annotations
+
+import email.policy
+import os.path
+import re
+from email.generator import BytesGenerator
+from email.parser import BytesParser
+
+from wheel.cli import WheelError
+from wheel.wheelfile import WheelFile
+
+DIST_INFO_RE = re.compile(r"^(?P<namever>(?P<name>.+?)-(?P<ver>\d.*?))\.dist-info$")
+
+
+def pack(directory: str, dest_dir: str, build_number: str | None) -> None:
+ """Repack a previously unpacked wheel directory into a new wheel file.
+
+ The .dist-info/WHEEL file must contain one or more tags so that the target
+ wheel file name can be determined.
+
+ :param directory: The unpacked wheel directory
+ :param dest_dir: Destination directory (defaults to the current directory)
+ """
+ # Find the .dist-info directory
+ dist_info_dirs = [
+ fn
+ for fn in os.listdir(directory)
+ if os.path.isdir(os.path.join(directory, fn)) and DIST_INFO_RE.match(fn)
+ ]
+ if len(dist_info_dirs) > 1:
+ raise WheelError(f"Multiple .dist-info directories found in {directory}")
+ elif not dist_info_dirs:
+ raise WheelError(f"No .dist-info directories found in {directory}")
+
+ # Determine the target wheel filename
+ dist_info_dir = dist_info_dirs[0]
+ name_version = DIST_INFO_RE.match(dist_info_dir).group("namever")
+
+ # Read the tags and the existing build number from .dist-info/WHEEL
+ wheel_file_path = os.path.join(directory, dist_info_dir, "WHEEL")
+ with open(wheel_file_path, "rb") as f:
+ info = BytesParser(policy=email.policy.compat32).parse(f)
+ tags: list[str] = info.get_all("Tag", [])
+ existing_build_number = info.get("Build")
+
+ if not tags:
+ raise WheelError(
+ f"No tags present in {dist_info_dir}/WHEEL; cannot determine target "
+ f"wheel filename"
+ )
+
+ # Set the wheel file name and add/replace/remove the Build tag in .dist-info/WHEEL
+ build_number = build_number if build_number is not None else existing_build_number
+ if build_number is not None:
+ del info["Build"]
+ if build_number:
+ info["Build"] = build_number
+ name_version += "-" + build_number
+
+ if build_number != existing_build_number:
+ with open(wheel_file_path, "wb") as f:
+ BytesGenerator(f, maxheaderlen=0).flatten(info)
+
+ # Reassemble the tags for the wheel file
+ tagline = compute_tagline(tags)
+
+ # Repack the wheel
+ wheel_path = os.path.join(dest_dir, f"{name_version}-{tagline}.whl")
+ with WheelFile(wheel_path, "w") as wf:
+ print(f"Repacking wheel as {wheel_path}...", end="", flush=True)
+ wf.write_files(directory)
+
+ print("OK")
+
+
+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/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/tags.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/tags.py
new file mode 100644
index 00000000..88da72e9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/tags.py
@@ -0,0 +1,139 @@
+from __future__ import annotations
+
+import email.policy
+import itertools
+import os
+from collections.abc import Iterable
+from email.parser import BytesParser
+
+from ..wheelfile import WheelFile
+
+
+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_tag: str | 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_tag: The build tag 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")
+ info = BytesParser(policy=email.policy.compat32).parsebytes(wheel_info)
+
+ 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: list[str] = info.get_all("Tag", [])
+ existing_build_tag = info.get("Build")
+
+ 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_tag != build:
+ msg = (
+ f"Incorrect filename '{build}' "
+ f"& *.dist-info/WHEEL '{existing_build_tag}' build numbers"
+ )
+ raise AssertionError(msg)
+
+ # Start changing as needed
+ if build_tag is not None:
+ build = build_tag
+
+ 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:
+ del info["Tag"], info["Build"]
+ for a, b, c in itertools.product(
+ final_python_tags, final_abi_tags, final_plat_tags
+ ):
+ info["Tag"] = f"{a}-{b}-{c}"
+ if build:
+ info["Build"] = build
+
+ 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.is_dir():
+ continue
+ if item.filename == f.dist_info_path + "/RECORD":
+ continue
+ if item.filename == f.dist_info_path + "/WHEEL":
+ fout.writestr(item, info.as_bytes())
+ else:
+ fout.writestr(item, fin.read(item))
+
+ if remove:
+ os.remove(original_wheel_path)
+
+ return final_wheel_name
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/unpack.py b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/unpack.py
new file mode 100644
index 00000000..d48840e6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_vendor/wheel/cli/unpack.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+from pathlib import Path
+
+from ..wheelfile import WheelFile
+
+
+def unpack(path: str, dest: str = ".") -> None:
+ """Unpack a wheel.
+
+ Wheel content will be unpacked to {dest}/{name}-{ver}, where {name}
+ is the package name and {ver} its version.
+
+ :param path: The path to the wheel.
+ :param dest: Destination directory (default to current directory).
+ """
+ with WheelFile(path) as wf:
+ namever = wf.parsed_filename.group("namever")
+ destination = Path(dest) / namever
+ print(f"Unpacking to: {destination}...", end="", flush=True)
+ for zinfo in wf.filelist:
+ wf.extract(zinfo, destination)
+
+ # Set permissions to the same values as they were set in the archive
+ # We have to do this manually due to
+ # https://github.com/python/cpython/issues/59999
+ permissions = zinfo.external_attr >> 16 & 0o777
+ destination.joinpath(zinfo.filename).chmod(permissions)
+
+ print("OK")