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