@@ -28,104 +28,139 @@ def str_from_console(s):
28
28
29
29
30
30
try :
31
+ import collections
32
+ import functools
31
33
import re
32
34
from typing import Iterator , List , Tuple
33
35
34
- from packaging .version import Version , _BaseVersion
36
+ from packaging import version as V
37
+ from packaging .version import VERSION_PATTERN , Version
35
38
36
39
###
37
40
### Legacy support for LooseVersion / LegacyVersion, e.g. 2.4-openbsd
38
41
### https://github.com/pypa/packaging/blob/21.3/packaging/version.py#L106-L115
39
42
### License: BSD, Accessed: Jan 14th, 2022
40
43
###
41
44
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 )
52
53
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 )
56
58
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
+ )
59
123
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 )
63
129
else :
64
- yield "*" + part
130
+ return None
65
131
66
- # ensure that alpha/beta/candidate are before final
67
- yield "*final"
132
+ def __str__ (self ):
133
+ # type: () -> str
134
+ parts = []
68
135
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 ))
75
139
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 ))
85
142
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 ) )
89
146
90
- parts .append (part )
147
+ # Post-release
148
+ if self .post is not None :
149
+ parts .append (".post{0}" .format (self .post ))
91
150
92
- return epoch , tuple (parts )
151
+ # Development release
152
+ if self .dev is not None :
153
+ parts .append (".dev{0}" .format (self .dev ))
93
154
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 ))
98
158
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 ))
101
162
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 )
129
164
130
165
LooseVersion = LegacyVersion
131
166
except ImportError :
0 commit comments