python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
setuptools/
_vendor/
packaging/
utils.py
       1  # This file is dual licensed under the terms of the Apache License, Version
       2  # 2.0, and the BSD License. See the LICENSE file in the root of this repository
       3  # for complete details.
       4  
       5  import re
       6  from typing import FrozenSet, NewType, Tuple, Union, cast
       7  
       8  from .tags import Tag, parse_tag
       9  from .version import InvalidVersion, Version
      10  
      11  BuildTag = Union[Tuple[()], Tuple[int, str]]
      12  NormalizedName = NewType("NormalizedName", str)
      13  
      14  
      15  class ESC[4;38;5;81mInvalidWheelFilename(ESC[4;38;5;149mValueError):
      16      """
      17      An invalid wheel filename was found, users should refer to PEP 427.
      18      """
      19  
      20  
      21  class ESC[4;38;5;81mInvalidSdistFilename(ESC[4;38;5;149mValueError):
      22      """
      23      An invalid sdist filename was found, users should refer to the packaging user guide.
      24      """
      25  
      26  
      27  _canonicalize_regex = re.compile(r"[-_.]+")
      28  # PEP 427: The build number must start with a digit.
      29  _build_tag_regex = re.compile(r"(\d+)(.*)")
      30  
      31  
      32  def canonicalize_name(name: str) -> NormalizedName:
      33      # This is taken from PEP 503.
      34      value = _canonicalize_regex.sub("-", name).lower()
      35      return cast(NormalizedName, value)
      36  
      37  
      38  def canonicalize_version(version: Union[Version, str]) -> str:
      39      """
      40      This is very similar to Version.__str__, but has one subtle difference
      41      with the way it handles the release segment.
      42      """
      43      if isinstance(version, str):
      44          try:
      45              parsed = Version(version)
      46          except InvalidVersion:
      47              # Legacy versions cannot be normalized
      48              return version
      49      else:
      50          parsed = version
      51  
      52      parts = []
      53  
      54      # Epoch
      55      if parsed.epoch != 0:
      56          parts.append(f"{parsed.epoch}!")
      57  
      58      # Release segment
      59      # NB: This strips trailing '.0's to normalize
      60      parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release)))
      61  
      62      # Pre-release
      63      if parsed.pre is not None:
      64          parts.append("".join(str(x) for x in parsed.pre))
      65  
      66      # Post-release
      67      if parsed.post is not None:
      68          parts.append(f".post{parsed.post}")
      69  
      70      # Development release
      71      if parsed.dev is not None:
      72          parts.append(f".dev{parsed.dev}")
      73  
      74      # Local version segment
      75      if parsed.local is not None:
      76          parts.append(f"+{parsed.local}")
      77  
      78      return "".join(parts)
      79  
      80  
      81  def parse_wheel_filename(
      82      filename: str,
      83  ) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]:
      84      if not filename.endswith(".whl"):
      85          raise InvalidWheelFilename(
      86              f"Invalid wheel filename (extension must be '.whl'): {filename}"
      87          )
      88  
      89      filename = filename[:-4]
      90      dashes = filename.count("-")
      91      if dashes not in (4, 5):
      92          raise InvalidWheelFilename(
      93              f"Invalid wheel filename (wrong number of parts): {filename}"
      94          )
      95  
      96      parts = filename.split("-", dashes - 2)
      97      name_part = parts[0]
      98      # See PEP 427 for the rules on escaping the project name
      99      if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
     100          raise InvalidWheelFilename(f"Invalid project name: {filename}")
     101      name = canonicalize_name(name_part)
     102      version = Version(parts[1])
     103      if dashes == 5:
     104          build_part = parts[2]
     105          build_match = _build_tag_regex.match(build_part)
     106          if build_match is None:
     107              raise InvalidWheelFilename(
     108                  f"Invalid build number: {build_part} in '{filename}'"
     109              )
     110          build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2)))
     111      else:
     112          build = ()
     113      tags = parse_tag(parts[-1])
     114      return (name, version, build, tags)
     115  
     116  
     117  def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]:
     118      if filename.endswith(".tar.gz"):
     119          file_stem = filename[: -len(".tar.gz")]
     120      elif filename.endswith(".zip"):
     121          file_stem = filename[: -len(".zip")]
     122      else:
     123          raise InvalidSdistFilename(
     124              f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
     125              f" {filename}"
     126          )
     127  
     128      # We are requiring a PEP 440 version, which cannot contain dashes,
     129      # so we split on the last dash.
     130      name_part, sep, version_part = file_stem.rpartition("-")
     131      if not sep:
     132          raise InvalidSdistFilename(f"Invalid sdist filename: {filename}")
     133  
     134      name = canonicalize_name(name_part)
     135      version = Version(version_part)
     136      return (name, version)