@@ -28,104 +28,139 @@ def str_from_console(s):
2828
2929
3030try :
31+ import collections
32+ import functools
3133 import re
3234 from typing import Iterator , List , Tuple
3335
34- from packaging .version import Version , _BaseVersion
36+ from packaging import version as V
37+ from packaging .version import VERSION_PATTERN , Version
3538
3639 ###
3740 ### Legacy support for LooseVersion / LegacyVersion, e.g. 2.4-openbsd
3841 ### https://github.com/pypa/packaging/blob/21.3/packaging/version.py#L106-L115
3942 ### License: BSD, Accessed: Jan 14th, 2022
4043 ###
4144
42- LegacyCmpKey = Tuple [int , Tuple [str , ...]]
43-
44- _legacy_version_component_re = re .compile (r"(\d+ | [a-z]+ | \.| -)" , re .VERBOSE )
45- _legacy_version_replacement_map = {
46- "pre" : "c" ,
47- "preview" : "c" ,
48- "-" : "final-" ,
49- "rc" : "c" ,
50- "dev" : "@" ,
51- }
45+ @functools .total_ordering
46+ class _VersionCmpMixin :
47+ # Credit: @layday
48+ # Link: https://github.com/pypa/packaging/issues/465#issuecomment-1013715662
49+ def __eq__ (self , other : object ) -> bool :
50+ if isinstance (other , str ):
51+ other = self .__class__ (other )
52+ return super ().__eq__ (other )
5253
53- def _parse_version_parts (s : str ) -> Iterator [str ]:
54- for part in _legacy_version_component_re .split (s ):
55- part = _legacy_version_replacement_map .get (part , part )
54+ def __lt__ (self , other : object ) -> bool :
55+ if isinstance (other , str ):
56+ other = self .__class__ (other )
57+ return super ().__lt__ (other )
5658
57- if not part or part == "." :
58- continue
59+ _Version = collections .namedtuple (
60+ "_Version" , ["epoch" , "release" , "dev" , "pre" , "post" , "local" , "platform" ]
61+ )
62+
63+ def _cmpkey (
64+ epoch , # type: int
65+ release , # type: Tuple[int, ...]
66+ pre , # type: Optional[Tuple[str, int]]
67+ post , # type: Optional[Tuple[str, int]]
68+ dev , # type: Optional[Tuple[str, int]]
69+ local , # type: Optional[Tuple[SubLocalType]]
70+ platform , # type: Optional[Tuple[SubLocalType]]
71+ ):
72+ return V ._cmpkey (epoch , release , pre , post , dev , local ) + (
73+ tuple (
74+ (i , "" ) if isinstance (i , int ) else (V .NegativeInfinity , i )
75+ for i in platform
76+ )
77+ )
78+
79+ class LegacyVersion (Version ):
80+ _regex = re .compile
81+ _regex = re .compile (
82+ r"^\s*"
83+ + VERSION_PATTERN
84+ + r"(?:(?P<platform>(?:[-_\.][a-z0-9]+)*))? # platform version"
85+ + r"\s*$" ,
86+ re .VERBOSE | re .IGNORECASE ,
87+ )
88+
89+ def __init__ (self , version ):
90+ # type: (str) -> None
91+
92+ # Validate the version and parse it into pieces
93+ match = self ._regex .search (version )
94+ if not match :
95+ raise V .InvalidVersion ("Invalid version: '{0}'" .format (version ))
96+
97+ # Store the parsed out pieces of the version
98+ self ._version = _Version (
99+ epoch = int (match .group ("epoch" )) if match .group ("epoch" ) else 0 ,
100+ release = tuple (int (i ) for i in match .group ("release" ).split ("." )),
101+ pre = V ._parse_letter_version (match .group ("pre_l" ), match .group ("pre_n" )),
102+ post = V ._parse_letter_version (
103+ match .group ("post_l" ),
104+ match .group ("post_n1" ) or match .group ("post_n2" ),
105+ ),
106+ dev = V ._parse_letter_version (match .group ("dev_l" ), match .group ("dev_n" )),
107+ local = V ._parse_local_version (match .group ("local" )),
108+ platform = str (match .group ('platform' ))
109+ if match .group ("platform" )
110+ else "" ,
111+ )
112+
113+ # Generate a key which will be used for sorting
114+ self ._key = _cmpkey (
115+ self ._version .epoch ,
116+ self ._version .release ,
117+ self ._version .pre ,
118+ self ._version .post ,
119+ self ._version .dev ,
120+ self ._version .local ,
121+ self ._version .platform ,
122+ )
59123
60- if part [:1 ] in "0123456789" :
61- # pad for numeric comparison
62- yield part .zfill (8 )
124+ @property
125+ def platform (self ):
126+ # type: () -> Optional[str]
127+ if self ._version .platform :
128+ return "." .join (str (x ) for x in self ._version .platform )
63129 else :
64- yield "*" + part
130+ return None
65131
66- # ensure that alpha/beta/candidate are before final
67- yield "*final"
132+ def __str__ (self ):
133+ # type: () -> str
134+ parts = []
68135
69- def _legacy_cmpkey (version : str ) -> LegacyCmpKey :
70- # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
71- # greater than or equal to 0. This will effectively put the LegacyVersion,
72- # which uses the defacto standard originally implemented by setuptools,
73- # as before all PEP 440 versions.
74- epoch = - 1
136+ # Epoch
137+ if self .epoch != 0 :
138+ parts .append ("{0}!" .format (self .epoch ))
75139
76- # This scheme is taken from pkg_resources.parse_version setuptools prior to
77- # it's adoption of the packaging library.
78- parts : List [str ] = []
79- for part in _parse_version_parts (version .lower ()):
80- if part .startswith ("*" ):
81- # remove "-" before a prerelease tag
82- if part < "*final" :
83- while parts and parts [- 1 ] == "*final-" :
84- parts .pop ()
140+ # Release segment
141+ parts .append ("." .join (str (x ) for x in self .release ))
85142
86- # remove trailing zeros from each series of numeric parts
87- while parts and parts [ - 1 ] == "00000000" :
88- parts . pop ( )
143+ # Pre-release
144+ if self . pre is not None :
145+ parts . append ( "" . join ( str ( x ) for x in self . pre ) )
89146
90- parts .append (part )
147+ # Post-release
148+ if self .post is not None :
149+ parts .append (".post{0}" .format (self .post ))
91150
92- return epoch , tuple (parts )
151+ # Development release
152+ if self .dev is not None :
153+ parts .append (".dev{0}" .format (self .dev ))
93154
94- class LegacyVersion (_BaseVersion ):
95- def __init__ (self , version : str ) -> None :
96- self ._version = str (version )
97- self ._key = _legacy_cmpkey (self ._version )
155+ # Local version segment
156+ if self .local is not None :
157+ parts .append ("+{0}" .format (self .local ))
98158
99- def __str__ (self ) -> str :
100- return self ._version
159+ # Platform version segment
160+ if self .platform is not None :
161+ parts .append ("_{0}" .format (self .platform ))
101162
102- def __lt__ (self , other ):
103- if isinstance (other , str ):
104- other = LegacyVersion (other )
105- return super ().__lt__ (other )
106-
107- def __eq__ (self , other ) -> bool :
108- if isinstance (other , str ):
109- other = LegacyVersion (other )
110- if not isinstance (other , LegacyVersion ):
111- return NotImplemented
112-
113- return self ._key == other ._key
114-
115- def __repr__ (self ) -> str :
116- return "<LegacyVersion({0})>" .format (repr (str (self )))
117-
118- @property
119- def public (self ) -> str :
120- return self ._version
121-
122- @property
123- def base_version (self ) -> str :
124- return self ._version
125-
126- @property
127- def epoch (self ) -> int :
128- return - 1
163+ return "" .join (parts )
129164
130165 LooseVersion = LegacyVersion
131166except ImportError :
0 commit comments