44#
55# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
66
7- import functools
8- from functools import total_ordering
9-
107import attr
118import semantic_version
129from packaging import version as packaging_version
@@ -59,7 +56,7 @@ def is_valid_alpine_version(s):
5956 return str (i ) == left
6057
6158
62- @attr .s (frozen = True , order = False , hash = True )
59+ @attr .s (frozen = True , order = True , eq = True , hash = True )
6360class Version :
6461 """
6562 Base version mixin to subclass for each version syntax implementation.
@@ -73,14 +70,16 @@ class Version:
7370 """
7471
7572 # the original string used to build this Version
76- string = attr .ib (type = str )
73+ string = attr .ib (type = str , eq = False , order = False , hash = False )
7774
7875 # the normalized string for this Version, stored without spaces and
7976 # lowercased. Any leading v is removed too.
80- normalized_string = attr .ib (type = str , default = None , repr = False )
77+ normalized_string = attr .ib (
78+ type = str , default = None , repr = False , eq = False , order = False , hash = False
79+ )
8180
8281 # a comparable scheme-specific version object constructed from the version string
83- value = attr .ib (default = None , repr = False )
82+ value = attr .ib (default = None , repr = False , eq = True , order = True , hash = True )
8483
8584 def __attrs_post_init__ (self ):
8685 normalized_string = self .normalize (self .string )
@@ -110,7 +109,7 @@ def normalize(cls, string):
110109 Return a normalized version string from ``string ``. Subclass can override.
111110 """
112111 # FIXME: Is removing spaces and strip v the right thing to do?
113- return remove_spaces (string ).rstrip ( "v " ). strip ( )
112+ return remove_spaces (string ).lstrip ( "vV" )
114113
115114 @classmethod
116115 def build_value (self , string ):
@@ -133,33 +132,7 @@ def satisfies(self, constraint):
133132 def __str__ (self ):
134133 return str (self .value )
135134
136- def __eq__ (self , other ):
137- if not isinstance (other , self .__class__ ):
138- return NotImplemented
139- return self .value .__eq__ (other .value )
140-
141- def __lt__ (self , other ):
142- if not isinstance (other , self .__class__ ):
143- return NotImplemented
144- return self .value .__lt__ (other .value )
145-
146- def __gt__ (self , other ):
147- if not isinstance (other , self .__class__ ):
148- return NotImplemented
149- return self .value .__gt__ (other .value )
150-
151- def __le__ (self , other ):
152- if not isinstance (other , self .__class__ ):
153- return NotImplemented
154- return self .value .__le__ (other .value )
155-
156- def __ge__ (self , other ):
157- if not isinstance (other , self .__class__ ):
158- return NotImplemented
159- return self .value .__ge__ (other .value )
160-
161135
162- @attr .s (frozen = True , order = False , hash = True )
163136class GenericVersion (Version ):
164137 @classmethod
165138 def is_valid (cls , string ):
@@ -174,13 +147,13 @@ def is_valid(cls, string):
174147 return super (GenericVersion , cls ).is_valid (string )
175148
176149
177- @attr .s (frozen = True , order = False , eq = False , hash = True )
178150class PypiVersion (Version ):
179151 """
180152 Python PEP 440 version as implemented in packaging with fallback to "legacy"
181153 """
182154
183155 # TODO: ensure we deal with triple equal
156+ # TODO: use packvers and handle legacy versions
184157
185158 @classmethod
186159 def build_value (cls , string ):
@@ -199,18 +172,22 @@ def is_valid(cls, string):
199172 except packaging_version .InvalidVersion :
200173 return False
201174
202- return False
175+
176+ class EnhancedSemanticVersion (semantic_version .Version ):
177+ @property
178+ def precedence_key (self ):
179+ key = super (EnhancedSemanticVersion , self ).precedence_key
180+ return key + (self .build or ())
203181
204182
205- @attr .s (frozen = True , order = False , eq = False , hash = True )
206183class SemverVersion (Version ):
207184 """
208185 Strict semver v2.0 with 3 segments.
209186 """
210187
211188 @classmethod
212189 def build_value (cls , string ):
213- return semantic_version . Version .coerce (string )
190+ return EnhancedSemanticVersion .coerce (string )
214191
215192 @classmethod
216193 def is_valid (cls , string ):
@@ -241,13 +218,13 @@ def build(self):
241218 return self .value and self .value .build
242219
243220 def next_major (self ):
244- return self .value and self .value .next_major ()
221+ return self .value and SemverVersion ( str ( self .value .next_major ()) )
245222
246223 def next_minor (self ):
247- return self .value and self .value .next_minor ()
224+ return self .value and SemverVersion ( str ( self .value .next_minor ()) )
248225
249226 def next_patch (self ):
250- return self .value and self .value .next_patch ()
227+ return self .value and SemverVersion ( str ( self .value .next_patch ()) )
251228
252229
253230def is_even (s ):
@@ -265,7 +242,6 @@ def is_even(s):
265242 return (int (s ) % 2 ) == 0
266243
267244
268- @attr .s (frozen = True , order = False , eq = False , hash = True )
269245class NginxVersion (SemverVersion ):
270246 """
271247 Semver with 3 segments and extra attribute for stable vs. unstable branches
@@ -279,7 +255,6 @@ def is_stable(self):
279255 return is_even (self .minor )
280256
281257
282- @attr .s (frozen = True , order = False , eq = False , hash = True )
283258class RubygemsVersion (Version ):
284259 """
285260 Rubygems encourages semver version but does not enforce it.
@@ -296,7 +271,6 @@ def is_valid(cls, string):
296271 return gem .GemVersion .is_correct (string )
297272
298273
299- @attr .s (frozen = True , order = False , eq = False , hash = True )
300274class ArchLinuxVersion (Version ):
301275 def __eq__ (self , other ):
302276 if not isinstance (other , self .__class__ ):
@@ -324,7 +298,6 @@ def __ge__(self, other):
324298 return arch .vercmp (self .value , other .value ) >= 0
325299
326300
327- @attr .s (frozen = True , order = False , eq = False , hash = True )
328301class DebianVersion (Version ):
329302 @classmethod
330303 def build_value (cls , string ):
@@ -335,7 +308,6 @@ def is_valid(cls, string):
335308 return debian .Version .is_valid (string )
336309
337310
338- @attr .s (frozen = True , order = False , eq = False , hash = True )
339311class MavenVersion (Version ):
340312 # See https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html
341313 # https://github.com/apache/maven/tree/master/maven-artifact/src/main/java/org/apache/maven/artifact/versioning
@@ -344,10 +316,16 @@ class MavenVersion(Version):
344316 def build_value (cls , string ):
345317 return maven .Version (string )
346318
319+ @classmethod
320+ def is_valid (cls , string ):
321+ try :
322+ cls .build_value (string )
323+ return True
324+ except ValueError :
325+ return False
326+
347327
348328# We will use total ordering to sort the versions, since these versions also consider prereleases.
349- @attr .s (frozen = True , order = False , eq = False , hash = True )
350- @functools .total_ordering
351329class NugetVersion (Version ):
352330 # See https://docs.microsoft.com/en-us/nuget/concepts/package-versioning
353331
@@ -363,33 +341,17 @@ def is_valid(cls, string):
363341 except ValueError :
364342 return False
365343
366- def __str__ (self ):
367- return str (self .string )
368-
369- def __lt__ (self , other ):
370- return nuget .Version .from_string (self .string ) < nuget .Version .from_string (other .string )
371-
372- def __eq__ (self , other ):
373- return nuget .Version .from_string (self .string ) == nuget .Version .from_string (other .string )
374-
375344
376- @attr .s (frozen = True , order = False , eq = False , hash = True )
377345class RpmVersion (Version ):
378346 """
379347 Represent an RPM version.
380-
381- For example::
382-
383- # 1:1.1.4|>=2.8.16|<=2.8.16-z
384348 """
385349
386350 @classmethod
387351 def build_value (cls , string ):
388352 return rpm .RpmVersion .from_string (string )
389353
390354
391- @total_ordering
392- @attr .s (frozen = True , order = False , eq = False , hash = True )
393355class GentooVersion (Version ):
394356 @classmethod
395357 def is_valid (cls , string ):
@@ -405,44 +367,30 @@ def __lt__(self, other):
405367 return NotImplemented
406368 return gentoo .vercmp (self .value , other .value ) == - 1
407369
408-
409- @attr .s (frozen = True , order = False , eq = False , hash = True )
410- class AlpineLinuxVersion (Version ):
411- @classmethod
412- def is_valid (cls , string ):
413- return is_valid_alpine_version (string ) and gentoo .is_valid (string )
414-
415- def __eq__ (self , other ):
370+ def __gt__ (self , other ):
416371 if not isinstance (other , self .__class__ ):
417372 return NotImplemented
418- return gentoo .vercmp (self .value , other .value ) == 0
373+ return gentoo .vercmp (self .value , other .value ) == 1
419374
420- def __lt__ (self , other ):
421- if not isinstance (other , self .__class__ ):
422- return NotImplemented
423- return gentoo .vercmp (self .value , other .value ) < 0
424375
425- def __gt__ ( self , other ):
426- if not isinstance ( other , self . __class__ ):
427- return NotImplemented
428- return gentoo . vercmp ( self . value , other . value ) > 0
376+ class AlpineLinuxVersion ( GentooVersion ):
377+ @ classmethod
378+ def is_valid ( cls , string ):
379+ return is_valid_alpine_version ( string ) and gentoo . is_valid ( string )
429380
430381
431- @attr .s (frozen = True , order = False , eq = False , hash = True )
432382class ComposerVersion (SemverVersion ):
433383 @classmethod
434384 def build_value (cls , string ):
435- return semantic_version . Version . coerce (string .lstrip ("vV" ))
385+ return super (). build_value (string .lstrip ("vV" ))
436386
437387
438- @attr .s (frozen = True , order = False , eq = False , hash = True )
439388class GolangVersion (SemverVersion ):
440389 @classmethod
441390 def build_value (cls , string ):
442- return semantic_version . Version . coerce (string .lstrip ("vV" ))
391+ return super (). build_value (string .lstrip ("vV" ))
443392
444393
445- @attr .s (frozen = True , order = False , eq = False , hash = True )
446394class LegacyOpensslVersion (Version ):
447395 """
448396 Represent an Legacy Openssl Version.
@@ -558,7 +506,6 @@ def is_prerelease(self):
558506 return self .patch .startswith (("-beta" , "-alpha" ))
559507
560508
561- @attr .s (frozen = True , order = False , eq = False , hash = True )
562509class OpensslVersion (Version ):
563510 """
564511 Internally tracks two types of openssl versions
@@ -653,8 +600,6 @@ def __ge__(self, other):
653600 return isinstance (self .value , SemverVersion )
654601
655602
656- @attr .s (frozen = True , order = False , eq = False , hash = True )
657- @total_ordering
658603class ConanVersion (Version ):
659604 @classmethod
660605 def build_value (cls , string ):
@@ -740,38 +685,21 @@ def next_patch(self):
740685 def bump (self , index ):
741686 return self .value and self .value .bump (index )
742687
743- def __eq__ (self , other ):
744- if other is None :
745- return False
746- if not isinstance (other , ConanVersion ):
747- other = ConanVersion .build_value (other )
748- return self .value == other
749- return self .value == other .value
750688
751- def __lt__ (self , other ):
752- if other is None :
753- return False
754- if not isinstance (other , ConanVersion ):
755- other = ConanVersion (str (other ))
756- return self .value < other .value
757-
758- def __le__ (self , other ):
759- if other is None :
760- return False
761- if not isinstance (other , ConanVersion ):
762- other = ConanVersion (str (other ))
763- return self .value <= other .value
764-
765- def __gt__ (self , other ):
766- if other is None :
767- return False
768- if not isinstance (other , ConanVersion ):
769- other = ConanVersion (str (other ))
770- return self .value > other .value
771-
772- def __ge__ (self , other ):
773- if other is None :
774- return False
775- if not isinstance (other , ConanVersion ):
776- other = ConanVersion (str (other ))
777- return self .value >= other .value
689+ AVAILABLE_VERSIONS = [
690+ SemverVersion ,
691+ GolangVersion ,
692+ PypiVersion ,
693+ GenericVersion ,
694+ ComposerVersion ,
695+ NginxVersion ,
696+ ArchLinuxVersion ,
697+ DebianVersion ,
698+ RpmVersion ,
699+ MavenVersion ,
700+ NugetVersion ,
701+ GentooVersion ,
702+ OpensslVersion ,
703+ LegacyOpensslVersion ,
704+ AlpineLinuxVersion ,
705+ ]
0 commit comments