@@ -371,29 +371,80 @@ def bump_build(self, token="build"):
371371 build = cls ._increment_string (self ._build or (token or "build" ) + ".0" )
372372 return cls (self ._major , self ._minor , self ._patch , self ._prerelease , build )
373373
374+ def compare (self , other ):
375+ """
376+ Compare self with other.
377+
378+ :param other: the second version (can be string, a dict, tuple/list, or
379+ a VersionInfo instance)
380+ :return: The return value is negative if ver1 < ver2,
381+ zero if ver1 == ver2 and strictly positive if ver1 > ver2
382+ :rtype: int
383+
384+ >>> semver.VersionInfo.parse("1.0.0").compare("2.0.0")
385+ -1
386+ >>> semver.VersionInfo.parse("2.0.0").compare("1.0.0")
387+ 1
388+ >>> semver.VersionInfo.parse("2.0.0").compare("2.0.0")
389+ 0
390+ >>> semver.VersionInfo.parse("2.0.0").compare(dict(major=2, minor=0, patch=0))
391+ 0
392+ """
393+ cls = type (self )
394+ if isinstance (other , str ):
395+ other = cls .parse (other )
396+ elif isinstance (other , dict ):
397+ other = cls (** other )
398+ elif isinstance (other , (tuple , list )):
399+ other = cls (* other )
400+ elif not isinstance (other , cls ):
401+ raise TypeError (
402+ "Expected str or {} instance, but got {}" .format (
403+ cls .__name__ , type (other )
404+ )
405+ )
406+
407+ v1 = self .to_tuple ()[:3 ]
408+ v2 = other .to_tuple ()[:3 ]
409+ x = cmp (v1 , v2 )
410+ if x :
411+ return x
412+
413+ rc1 , rc2 = self .prerelease , other .prerelease
414+ rccmp = _nat_cmp (rc1 , rc2 )
415+
416+ if not rccmp :
417+ return 0
418+ if not rc1 :
419+ return 1
420+ elif not rc2 :
421+ return - 1
422+
423+ return rccmp
424+
374425 @comparator
375426 def __eq__ (self , other ):
376- return _compare_by_keys ( self .to_dict (), _to_dict ( other ) ) == 0
427+ return self .compare ( other ) == 0
377428
378429 @comparator
379430 def __ne__ (self , other ):
380- return _compare_by_keys ( self .to_dict (), _to_dict ( other ) ) != 0
431+ return self .compare ( other ) != 0
381432
382433 @comparator
383434 def __lt__ (self , other ):
384- return _compare_by_keys ( self .to_dict (), _to_dict ( other ) ) < 0
435+ return self .compare ( other ) < 0
385436
386437 @comparator
387438 def __le__ (self , other ):
388- return _compare_by_keys ( self .to_dict (), _to_dict ( other ) ) <= 0
439+ return self .compare ( other ) <= 0
389440
390441 @comparator
391442 def __gt__ (self , other ):
392- return _compare_by_keys ( self .to_dict (), _to_dict ( other ) ) > 0
443+ return self .compare ( other ) > 0
393444
394445 @comparator
395446 def __ge__ (self , other ):
396- return _compare_by_keys ( self .to_dict (), _to_dict ( other ) ) >= 0
447+ return self .compare ( other ) >= 0
397448
398449 def __repr__ (self ):
399450 s = ", " .join ("%s=%r" % (key , val ) for key , val in self .to_dict ().items ())
@@ -424,6 +475,53 @@ def finalize_version(self):
424475 cls = type (self )
425476 return cls (self .major , self .minor , self .patch )
426477
478+ def match (self , match_expr ):
479+ """
480+ Compare self to match a match expression.
481+
482+ :param str match_expr: operator and version; valid operators are
483+ < smaller than
484+ > greater than
485+ >= greator or equal than
486+ <= smaller or equal than
487+ == equal
488+ != not equal
489+ :return: True if the expression matches the version, otherwise False
490+ :rtype: bool
491+
492+ >>> semver.VersionInfo.parse("2.0.0").match(">=1.0.0")
493+ True
494+ >>> semver.VersionInfo.parse("1.0.0").match(">1.0.0")
495+ False
496+ """
497+ prefix = match_expr [:2 ]
498+ if prefix in (">=" , "<=" , "==" , "!=" ):
499+ match_version = match_expr [2 :]
500+ elif prefix and prefix [0 ] in (">" , "<" ):
501+ prefix = prefix [0 ]
502+ match_version = match_expr [1 :]
503+ else :
504+ raise ValueError (
505+ "match_expr parameter should be in format <op><ver>, "
506+ "where <op> is one of "
507+ "['<', '>', '==', '<=', '>=', '!=']. "
508+ "You provided: %r" % match_expr
509+ )
510+
511+ possibilities_dict = {
512+ ">" : (1 ,),
513+ "<" : (- 1 ,),
514+ "==" : (0 ,),
515+ "!=" : (- 1 , 1 ),
516+ ">=" : (0 , 1 ),
517+ "<=" : (- 1 , 0 ),
518+ }
519+
520+ possibilities = possibilities_dict [prefix ]
521+ cmp_res = self .compare (match_version )
522+
523+ return cmp_res in possibilities
524+
427525 @staticmethod
428526 def parse (version ):
429527 """
@@ -495,14 +593,6 @@ def isvalid(cls, version):
495593 return False
496594
497595
498- def _to_dict (obj ):
499- if isinstance (obj , VersionInfo ):
500- return obj .to_dict ()
501- elif isinstance (obj , tuple ):
502- return VersionInfo (* obj ).to_dict ()
503- return obj
504-
505-
506596@deprecated (replace = "semver.VersionInfo.parse" , version = "2.10.0" )
507597def parse_version_info (version ):
508598 """
@@ -560,25 +650,7 @@ def cmp_prerelease_tag(a, b):
560650 return cmp (len (a ), len (b ))
561651
562652
563- def _compare_by_keys (d1 , d2 ):
564- for key in ["major" , "minor" , "patch" ]:
565- v = cmp (d1 .get (key ), d2 .get (key ))
566- if v :
567- return v
568-
569- rc1 , rc2 = d1 .get ("prerelease" ), d2 .get ("prerelease" )
570- rccmp = _nat_cmp (rc1 , rc2 )
571-
572- if not rccmp :
573- return 0
574- if not rc1 :
575- return 1
576- elif not rc2 :
577- return - 1
578-
579- return rccmp
580-
581-
653+ @deprecated (version = "2.10.0" )
582654def compare (ver1 , ver2 ):
583655 """
584656 Compare two versions strings.
@@ -596,13 +668,11 @@ def compare(ver1, ver2):
596668 >>> semver.compare("2.0.0", "2.0.0")
597669 0
598670 """
599-
600- v1 = VersionInfo .parse (ver1 ).to_dict ()
601- v2 = VersionInfo .parse (ver2 ).to_dict ()
602-
603- return _compare_by_keys (v1 , v2 )
671+ v1 = VersionInfo .parse (ver1 )
672+ return v1 .compare (ver2 )
604673
605674
675+ @deprecated (version = "2.10.0" )
606676def match (version , match_expr ):
607677 """
608678 Compare two versions strings through a comparison.
@@ -623,33 +693,8 @@ def match(version, match_expr):
623693 >>> semver.match("1.0.0", ">1.0.0")
624694 False
625695 """
626- prefix = match_expr [:2 ]
627- if prefix in (">=" , "<=" , "==" , "!=" ):
628- match_version = match_expr [2 :]
629- elif prefix and prefix [0 ] in (">" , "<" ):
630- prefix = prefix [0 ]
631- match_version = match_expr [1 :]
632- else :
633- raise ValueError (
634- "match_expr parameter should be in format <op><ver>, "
635- "where <op> is one of "
636- "['<', '>', '==', '<=', '>=', '!=']. "
637- "You provided: %r" % match_expr
638- )
639-
640- possibilities_dict = {
641- ">" : (1 ,),
642- "<" : (- 1 ,),
643- "==" : (0 ,),
644- "!=" : (- 1 , 1 ),
645- ">=" : (0 , 1 ),
646- "<=" : (- 1 , 0 ),
647- }
648-
649- possibilities = possibilities_dict [prefix ]
650- cmp_res = compare (version , match_version )
651-
652- return cmp_res in possibilities
696+ ver = VersionInfo .parse (version )
697+ return ver .match (match_expr )
653698
654699
655700def max_ver (ver1 , ver2 ):
@@ -664,9 +709,10 @@ def max_ver(ver1, ver2):
664709 >>> semver.max_ver("1.0.0", "2.0.0")
665710 '2.0.0'
666711 """
667- cmp_res = compare (ver1 , ver2 )
668- if cmp_res == 0 or cmp_res == 1 :
669- return ver1
712+ ver1 = VersionInfo .parse (ver1 )
713+ cmp_res = ver1 .compare (ver2 )
714+ if cmp_res >= 0 :
715+ return str (ver1 )
670716 else :
671717 return ver2
672718
@@ -683,9 +729,10 @@ def min_ver(ver1, ver2):
683729 >>> semver.min_ver("1.0.0", "2.0.0")
684730 '1.0.0'
685731 """
686- cmp_res = compare (ver1 , ver2 )
687- if cmp_res == 0 or cmp_res == - 1 :
688- return ver1
732+ ver1 = VersionInfo .parse (ver1 )
733+ cmp_res = ver1 .compare (ver2 )
734+ if cmp_res <= 0 :
735+ return str (ver1 )
689736 else :
690737 return ver2
691738
0 commit comments