1- # Vendored from https://github.com/pypa/packaging/blob/main/packaging/_structures.py
2- # and https://github.com/pypa/packaging/blob/main/packaging/_structures.py
3- # changeset ae891fd74d6dd4c6063bb04f2faeadaac6fc6313
4- # 04/30/2021
1+ # Vendored from https://github.com/pypa/packaging/blob/main/src/packaging/_structures.py
2+ # and https://github.com/pypa/packaging/blob/main/src/packaging/version.py
3+ # changeset 24e5350b2ff3c5c7a36676c2af5f2cb39fd1baf8
54
65# This file is dual licensed under the terms of the Apache License, Version
76# 2.0, and the BSD License. Licence at LICENSES/PACKAGING_LICENSE
87from __future__ import annotations
98
10- import collections
11- from collections.abc import (
12- Callable,
13- Iterator,
14- )
9+ from collections.abc import Callable
1510import itertools
1611import re
1712from typing import (
13+ Any,
14+ NamedTuple,
1815 SupportsInt,
19- Tuple,
2016 Union,
2117)
22- import warnings
2318
24- __all__ = ["parse ", "Version ", "LegacyVersion ", "InvalidVersion", "VERSION_PATTERN "]
19+ __all__ = ["VERSION_PATTERN ", "InvalidVersion ", "Version ", "parse "]
2520
2621
2722class InfinityType:
@@ -40,9 +35,6 @@ def __le__(self, other: object) -> bool:
4035 def __eq__(self, other: object) -> bool:
4136 return isinstance(other, type(self))
4237
43- def __ne__(self, other: object) -> bool:
44- return not isinstance(other, type(self))
45-
4638 def __gt__(self, other: object) -> bool:
4739 return True
4840
@@ -72,9 +64,6 @@ def __le__(self, other: object) -> bool:
7264 def __eq__(self, other: object) -> bool:
7365 return isinstance(other, type(self))
7466
75- def __ne__(self, other: object) -> bool:
76- return not isinstance(other, type(self))
77-
7867 def __gt__(self, other: object) -> bool:
7968 return False
8069
@@ -88,45 +77,39 @@ def __neg__(self: object) -> InfinityType:
8877NegativeInfinity = NegativeInfinityType()
8978
9079
91- InfiniteTypes = Union[InfinityType, NegativeInfinityType ]
92- PrePostDevType = Union[InfiniteTypes, tuple[str, int]]
93- SubLocalType = Union[InfiniteTypes, int, str]
94- LocalType = Union[
80+ LocalType = tuple[ Union[int, str], ... ]
81+
82+ CmpPrePostDevType = Union[InfinityType, NegativeInfinityType, tuple[ str, int] ]
83+ CmpLocalType = Union[
9584 NegativeInfinityType,
96- tuple[
97- Union[
98- SubLocalType,
99- tuple[SubLocalType, str],
100- tuple[NegativeInfinityType, SubLocalType],
101- ],
102- ...,
103- ],
85+ tuple[Union[tuple[int, str], tuple[NegativeInfinityType, Union[int, str]]], ...],
10486]
10587CmpKey = tuple[
106- int, tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
107- ]
108- LegacyCmpKey = tuple[int, tuple[str, ...]]
109- VersionComparisonMethod = Callable[
110- [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
88+ int,
89+ tuple[int, ...],
90+ CmpPrePostDevType,
91+ CmpPrePostDevType,
92+ CmpPrePostDevType,
93+ CmpLocalType,
11194]
95+ VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool]
11296
113- _Version = collections.namedtuple(
114- "_Version", ["epoch", "release", "dev", "pre", "post", "local"]
115- )
11697
98+ class _Version(NamedTuple):
99+ epoch: int
100+ release: tuple[int, ...]
101+ dev: tuple[str, int] | None
102+ pre: tuple[str, int] | None
103+ post: tuple[str, int] | None
104+ local: LocalType | None
117105
118- def parse(version: str) -> LegacyVersion | Version:
119- """
120- Parse the given version string and return either a :class:`Version` object
121- or a :class:`LegacyVersion` object depending on if the given version is
122- a valid PEP 440 version or a legacy version.
123- """
124- try:
125- return Version(version)
126- except InvalidVersion:
127- return LegacyVersion(version)
106+
107+ def parse(version: str) -> Version:
108+ return Version(version)
128109
129110
111+ # The docstring is from an older version of the packaging library to avoid
112+ # errors in the docstring validation.
130113class InvalidVersion(ValueError):
131114 """
132115 An invalid version was found, users should refer to PEP 440.
@@ -140,7 +123,7 @@ class InvalidVersion(ValueError):
140123
141124
142125class _BaseVersion:
143- _key: CmpKey | LegacyCmpKey
126+ _key: tuple[Any, ...]
144127
145128 def __hash__(self) -> int:
146129 return hash(self._key)
@@ -185,132 +168,16 @@ def __ne__(self, other: object) -> bool:
185168 return self._key != other._key
186169
187170
188- class LegacyVersion(_BaseVersion):
189- def __init__(self, version: str) -> None:
190- self._version = str(version)
191- self._key = _legacy_cmpkey(self._version)
192-
193- warnings.warn(
194- "Creating a LegacyVersion has been deprecated and will be "
195- "removed in the next major release.",
196- DeprecationWarning,
197- )
198-
199- def __str__(self) -> str:
200- return self._version
201-
202- def __repr__(self) -> str:
203- return f"<LegacyVersion('{self}')>"
204-
205- @property
206- def public(self) -> str:
207- return self._version
208-
209- @property
210- def base_version(self) -> str:
211- return self._version
212-
213- @property
214- def epoch(self) -> int:
215- return -1
216-
217- @property
218- def release(self) -> None:
219- return None
220-
221- @property
222- def pre(self) -> None:
223- return None
224-
225- @property
226- def post(self) -> None:
227- return None
228-
229- @property
230- def dev(self) -> None:
231- return None
232-
233- @property
234- def local(self) -> None:
235- return None
236-
237- @property
238- def is_prerelease(self) -> bool:
239- return False
240-
241- @property
242- def is_postrelease(self) -> bool:
243- return False
244-
245- @property
246- def is_devrelease(self) -> bool:
247- return False
248-
249-
250- _legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE)
251-
252- _legacy_version_replacement_map = {
253- "pre": "c",
254- "preview": "c",
255- "-": "final-",
256- "rc": "c",
257- "dev": "@",
258- }
259-
260-
261- def _parse_version_parts(s: str) -> Iterator[str]:
262- for part in _legacy_version_component_re.split(s):
263- mapped_part = _legacy_version_replacement_map.get(part, part)
264-
265- if not mapped_part or mapped_part == ".":
266- continue
267-
268- if mapped_part[:1] in "0123456789":
269- # pad for numeric comparison
270- yield mapped_part.zfill(8)
271- else:
272- yield "*" + mapped_part
273-
274- # ensure that alpha/beta/candidate are before final
275- yield "*final"
276-
277-
278- def _legacy_cmpkey(version: str) -> LegacyCmpKey:
279- # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
280- # greater than or equal to 0. This will effectively put the LegacyVersion,
281- # which uses the defacto standard originally implemented by setuptools,
282- # as before all PEP 440 versions.
283- epoch = -1
284-
285- # This scheme is taken from pkg_resources.parse_version setuptools prior to
286- # it's adoption of the packaging library.
287- parts: list[str] = []
288- for part in _parse_version_parts(version.lower()):
289- if part.startswith("*"):
290- # remove "-" before a prerelease tag
291- if part < "*final":
292- while parts and parts[-1] == "*final-":
293- parts.pop()
294-
295- # remove trailing zeros from each series of numeric parts
296- while parts and parts[-1] == "00000000":
297- parts.pop()
298-
299- parts.append(part)
300-
301- return epoch, tuple(parts)
302-
303-
304171# Deliberately not anchored to the start and end of the string, to make it
305172# easier for 3rd party code to reuse
306- VERSION_PATTERN = r"""
173+ _VERSION_PATTERN = r"""
307174 v?
308175 (?:
309176 (?:(?P<epoch>[0-9]+)!)? # epoch
310177 (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
311178 (?P<pre> # pre-release
312179 [-_\.]?
313- (?P<pre_l>(a|b|c|rc|alpha|beta| pre|preview) )
180+ (?P<pre_l>alpha|a|beta|b|preview| pre|c|rc )
314181 [-_\.]?
315182 (?P<pre_n>[0-9]+)?
316183 )?
@@ -334,9 +201,12 @@ def _legacy_cmpkey(version: str) -> LegacyCmpKey:
334201 (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
335202"""
336203
204+ VERSION_PATTERN = _VERSION_PATTERN
205+
337206
338207class Version(_BaseVersion):
339208 _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
209+ _key: CmpKey
340210
341211 def __init__(self, version: str) -> None:
342212 # Validate the version and parse it into pieces
@@ -377,11 +247,11 @@ def __str__(self) -> str:
377247 parts.append(f"{self.epoch}!")
378248
379249 # Release segment
380- parts.append(".".join([ str(x) for x in self.release] ))
250+ parts.append(".".join(str(x) for x in self.release))
381251
382252 # Pre-release
383253 if self.pre is not None:
384- parts.append("".join([ str(x) for x in self.pre] ))
254+ parts.append("".join(str(x) for x in self.pre))
385255
386256 # Post-release
387257 if self.post is not None:
@@ -399,18 +269,15 @@ def __str__(self) -> str:
399269
400270 @property
401271 def epoch(self) -> int:
402- _epoch: int = self._version.epoch
403- return _epoch
272+ return self._version.epoch
404273
405274 @property
406275 def release(self) -> tuple[int, ...]:
407- _release: tuple[int, ...] = self._version.release
408- return _release
276+ return self._version.release
409277
410278 @property
411279 def pre(self) -> tuple[str, int] | None:
412- _pre: tuple[str, int] | None = self._version.pre
413- return _pre
280+ return self._version.pre
414281
415282 @property
416283 def post(self) -> int | None:
@@ -423,7 +290,7 @@ def dev(self) -> int | None:
423290 @property
424291 def local(self) -> str | None:
425292 if self._version.local:
426- return ".".join([ str(x) for x in self._version.local] )
293+ return ".".join(str(x) for x in self._version.local)
427294 else:
428295 return None
429296
@@ -440,7 +307,7 @@ def base_version(self) -> str:
440307 parts.append(f"{self.epoch}!")
441308
442309 # Release segment
443- parts.append(".".join([ str(x) for x in self.release] ))
310+ parts.append(".".join(str(x) for x in self.release))
444311
445312 return "".join(parts)
446313
@@ -470,7 +337,7 @@ def micro(self) -> int:
470337
471338
472339def _parse_letter_version(
473- letter: str, number: str | bytes | SupportsInt
340+ letter: str | None , number: str | bytes | SupportsInt | None
474341) -> tuple[str, int] | None:
475342 if letter:
476343 # We consider there to be an implicit 0 in a pre-release if there is
@@ -507,10 +374,7 @@ def _parse_letter_version(
507374_local_version_separators = re.compile(r"[\._-]")
508375
509376
510- def _parse_local_version(local: str) -> LocalType | None:
511- """
512- Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
513- """
377+ def _parse_local_version(local: str | None) -> LocalType | None:
514378 if local is not None:
515379 return tuple(
516380 part.lower() if not part.isdigit() else int(part)
@@ -525,7 +389,7 @@ def _cmpkey(
525389 pre: tuple[str, int] | None,
526390 post: tuple[str, int] | None,
527391 dev: tuple[str, int] | None,
528- local: tuple[SubLocalType] | None,
392+ local: LocalType | None,
529393) -> CmpKey:
530394 # When we compare a release version, we want to compare it with all of the
531395 # trailing zeros removed. So we'll use a reverse the list, drop all the now
@@ -541,7 +405,7 @@ def _cmpkey(
541405 # if there is not a pre or a post segment. If we have one of those then
542406 # the normal sorting rules will handle this case correctly.
543407 if pre is None and post is None and dev is not None:
544- _pre: PrePostDevType = NegativeInfinity
408+ _pre: CmpPrePostDevType = NegativeInfinity
545409 # Versions without a pre-release (except as noted above) should sort after
546410 # those with one.
547411 elif pre is None:
@@ -551,21 +415,21 @@ def _cmpkey(
551415
552416 # Versions without a post segment should sort before those with one.
553417 if post is None:
554- _post: PrePostDevType = NegativeInfinity
418+ _post: CmpPrePostDevType = NegativeInfinity
555419
556420 else:
557421 _post = post
558422
559423 # Versions without a development segment should sort after those with one.
560424 if dev is None:
561- _dev: PrePostDevType = Infinity
425+ _dev: CmpPrePostDevType = Infinity
562426
563427 else:
564428 _dev = dev
565429
566430 if local is None:
567431 # Versions without a local segment should sort before those with one.
568- _local: LocalType = NegativeInfinity
432+ _local: CmpLocalType = NegativeInfinity
569433 else:
570434 # Versions with a local segment need that segment parsed to implement
571435 # the sorting rules in PEP440.
0 commit comments