annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/packaging/utils.py @ 69:33d812a61356

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