annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/packaging/utils.py @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
rev   line source
jpayne@68 1 # This file is dual licensed under the terms of the Apache License, Version
jpayne@68 2 # 2.0, and the BSD License. See the LICENSE file in the root of this repository
jpayne@68 3 # for complete details.
jpayne@68 4
jpayne@68 5 from __future__ import annotations
jpayne@68 6
jpayne@68 7 import functools
jpayne@68 8 import re
jpayne@68 9 from typing import NewType, Tuple, Union, cast
jpayne@68 10
jpayne@68 11 from .tags import Tag, parse_tag
jpayne@68 12 from .version import InvalidVersion, Version, _TrimmedRelease
jpayne@68 13
jpayne@68 14 BuildTag = Union[Tuple[()], Tuple[int, str]]
jpayne@68 15 NormalizedName = NewType("NormalizedName", str)
jpayne@68 16
jpayne@68 17
jpayne@68 18 class InvalidName(ValueError):
jpayne@68 19 """
jpayne@68 20 An invalid distribution name; users should refer to the packaging user guide.
jpayne@68 21 """
jpayne@68 22
jpayne@68 23
jpayne@68 24 class InvalidWheelFilename(ValueError):
jpayne@68 25 """
jpayne@68 26 An invalid wheel filename was found, users should refer to PEP 427.
jpayne@68 27 """
jpayne@68 28
jpayne@68 29
jpayne@68 30 class InvalidSdistFilename(ValueError):
jpayne@68 31 """
jpayne@68 32 An invalid sdist filename was found, users should refer to the packaging user guide.
jpayne@68 33 """
jpayne@68 34
jpayne@68 35
jpayne@68 36 # Core metadata spec for `Name`
jpayne@68 37 _validate_regex = re.compile(
jpayne@68 38 r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE
jpayne@68 39 )
jpayne@68 40 _canonicalize_regex = re.compile(r"[-_.]+")
jpayne@68 41 _normalized_regex = re.compile(r"^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$")
jpayne@68 42 # PEP 427: The build number must start with a digit.
jpayne@68 43 _build_tag_regex = re.compile(r"(\d+)(.*)")
jpayne@68 44
jpayne@68 45
jpayne@68 46 def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName:
jpayne@68 47 if validate and not _validate_regex.match(name):
jpayne@68 48 raise InvalidName(f"name is invalid: {name!r}")
jpayne@68 49 # This is taken from PEP 503.
jpayne@68 50 value = _canonicalize_regex.sub("-", name).lower()
jpayne@68 51 return cast(NormalizedName, value)
jpayne@68 52
jpayne@68 53
jpayne@68 54 def is_normalized_name(name: str) -> bool:
jpayne@68 55 return _normalized_regex.match(name) is not None
jpayne@68 56
jpayne@68 57
jpayne@68 58 @functools.singledispatch
jpayne@68 59 def canonicalize_version(
jpayne@68 60 version: Version | str, *, strip_trailing_zero: bool = True
jpayne@68 61 ) -> str:
jpayne@68 62 """
jpayne@68 63 Return a canonical form of a version as a string.
jpayne@68 64
jpayne@68 65 >>> canonicalize_version('1.0.1')
jpayne@68 66 '1.0.1'
jpayne@68 67
jpayne@68 68 Per PEP 625, versions may have multiple canonical forms, differing
jpayne@68 69 only by trailing zeros.
jpayne@68 70
jpayne@68 71 >>> canonicalize_version('1.0.0')
jpayne@68 72 '1'
jpayne@68 73 >>> canonicalize_version('1.0.0', strip_trailing_zero=False)
jpayne@68 74 '1.0.0'
jpayne@68 75
jpayne@68 76 Invalid versions are returned unaltered.
jpayne@68 77
jpayne@68 78 >>> canonicalize_version('foo bar baz')
jpayne@68 79 'foo bar baz'
jpayne@68 80 """
jpayne@68 81 return str(_TrimmedRelease(str(version)) if strip_trailing_zero else version)
jpayne@68 82
jpayne@68 83
jpayne@68 84 @canonicalize_version.register
jpayne@68 85 def _(version: str, *, strip_trailing_zero: bool = True) -> str:
jpayne@68 86 try:
jpayne@68 87 parsed = Version(version)
jpayne@68 88 except InvalidVersion:
jpayne@68 89 # Legacy versions cannot be normalized
jpayne@68 90 return version
jpayne@68 91 return canonicalize_version(parsed, strip_trailing_zero=strip_trailing_zero)
jpayne@68 92
jpayne@68 93
jpayne@68 94 def parse_wheel_filename(
jpayne@68 95 filename: str,
jpayne@68 96 ) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]:
jpayne@68 97 if not filename.endswith(".whl"):
jpayne@68 98 raise InvalidWheelFilename(
jpayne@68 99 f"Invalid wheel filename (extension must be '.whl'): {filename!r}"
jpayne@68 100 )
jpayne@68 101
jpayne@68 102 filename = filename[:-4]
jpayne@68 103 dashes = filename.count("-")
jpayne@68 104 if dashes not in (4, 5):
jpayne@68 105 raise InvalidWheelFilename(
jpayne@68 106 f"Invalid wheel filename (wrong number of parts): {filename!r}"
jpayne@68 107 )
jpayne@68 108
jpayne@68 109 parts = filename.split("-", dashes - 2)
jpayne@68 110 name_part = parts[0]
jpayne@68 111 # See PEP 427 for the rules on escaping the project name.
jpayne@68 112 if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
jpayne@68 113 raise InvalidWheelFilename(f"Invalid project name: {filename!r}")
jpayne@68 114 name = canonicalize_name(name_part)
jpayne@68 115
jpayne@68 116 try:
jpayne@68 117 version = Version(parts[1])
jpayne@68 118 except InvalidVersion as e:
jpayne@68 119 raise InvalidWheelFilename(
jpayne@68 120 f"Invalid wheel filename (invalid version): {filename!r}"
jpayne@68 121 ) from e
jpayne@68 122
jpayne@68 123 if dashes == 5:
jpayne@68 124 build_part = parts[2]
jpayne@68 125 build_match = _build_tag_regex.match(build_part)
jpayne@68 126 if build_match is None:
jpayne@68 127 raise InvalidWheelFilename(
jpayne@68 128 f"Invalid build number: {build_part} in {filename!r}"
jpayne@68 129 )
jpayne@68 130 build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2)))
jpayne@68 131 else:
jpayne@68 132 build = ()
jpayne@68 133 tags = parse_tag(parts[-1])
jpayne@68 134 return (name, version, build, tags)
jpayne@68 135
jpayne@68 136
jpayne@68 137 def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]:
jpayne@68 138 if filename.endswith(".tar.gz"):
jpayne@68 139 file_stem = filename[: -len(".tar.gz")]
jpayne@68 140 elif filename.endswith(".zip"):
jpayne@68 141 file_stem = filename[: -len(".zip")]
jpayne@68 142 else:
jpayne@68 143 raise InvalidSdistFilename(
jpayne@68 144 f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
jpayne@68 145 f" {filename!r}"
jpayne@68 146 )
jpayne@68 147
jpayne@68 148 # We are requiring a PEP 440 version, which cannot contain dashes,
jpayne@68 149 # so we split on the last dash.
jpayne@68 150 name_part, sep, version_part = file_stem.rpartition("-")
jpayne@68 151 if not sep:
jpayne@68 152 raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}")
jpayne@68 153
jpayne@68 154 name = canonicalize_name(name_part)
jpayne@68 155
jpayne@68 156 try:
jpayne@68 157 version = Version(version_part)
jpayne@68 158 except InvalidVersion as e:
jpayne@68 159 raise InvalidSdistFilename(
jpayne@68 160 f"Invalid sdist filename (invalid version): {filename!r}"
jpayne@68 161 ) from e
jpayne@68 162
jpayne@68 163 return (name, version)