Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Changelog
*unreleased*
~~~~~~~~~~~~

No unreleased changes.
* Added the ``packaging.wheelfile`` module for reading and creating wheel files
(:issue:`697`)

24.0 - 2024-03-10
~~~~~~~~~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The ``packaging`` library uses calendar-based versioning (``YY.N``).
requirements
metadata
tags
wheelfile
utils

.. toctree::
Expand Down
4 changes: 4 additions & 0 deletions docs/wheelfile.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Wheel Files
===========

.. currentmodule:: packaging.wheelfile
24 changes: 24 additions & 0 deletions src/packaging/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

import re
from collections.abc import Collection
from typing import NewType, Tuple, Union, cast

from .tags import Tag, parse_tag
Expand Down Expand Up @@ -40,6 +41,7 @@ class InvalidSdistFilename(ValueError):
_normalized_regex = re.compile(r"^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$")
# PEP 427: The build number must start with a digit.
_build_tag_regex = re.compile(r"(\d+)(.*)")
_dist_name_re = re.compile(r"[^a-z0-9.]+", re.IGNORECASE)


def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName:
Expand Down Expand Up @@ -102,6 +104,28 @@ def canonicalize_version(
return "".join(parts)


def make_wheel_filename(
name: str,
version: str | Version,
tags: Collection[Tag],
*,
build_tag: BuildTag | None = None,
) -> str:
if not tags:
raise ValueError("At least one tag is required")

name = canonicalize_name(name).replace("-", "_").lower()
version = canonicalize_version(version)
filename = f"{name}-{version}"
if build_tag:
filename = f"{filename}-{build_tag[0]}{build_tag[1]}"

interpreter_tags = ".".join(tag.interpreter for tag in tags)
abi_tags = ".".join(tag.abi for tag in tags)
platform_tags = ".".join(tag.platform for tag in tags)
return f"{filename}-{interpreter_tags}-{abi_tags}-{platform_tags}.whl"


def parse_wheel_filename(
filename: str,
) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
Expand Down
Loading