55import re
66import sys
77from dataclasses import asdict , dataclass
8- from typing import Any , Iterable , List , Mapping , Tuple , TypedDict , Union , overload
8+ from typing import Any , List , Mapping , Tuple , TypedDict , Union , overload
99
1010from typing_extensions import NotRequired , Self , TypeAlias
1111
@@ -40,7 +40,7 @@ class VersionDict(TypedDict):
4040]
4141
4242VersionDictLike : TypeAlias = Mapping [str , Union [int , PreRelease , BuildMetadata ]]
43- VersionTupleLike : TypeAlias = Iterable [Union [int , PreRelease , BuildMetadata ]]
43+ VersionTupleLike : TypeAlias = Tuple [Union [int , PreRelease , BuildMetadata ], ... ]
4444VersionLike : TypeAlias = Union ["Version" , str , VersionDictLike , VersionTupleLike ]
4545
4646
@@ -50,7 +50,7 @@ class Version:
5050
5151 Version format is: MAJOR.MINOR.PATCH[-PRERELEASE][+BUILDMETADATA]
5252
53- Based on https://semver.org/.
53+ Based on https://semver.org/ version 2.0.0 .
5454 """
5555
5656 major : int
@@ -266,11 +266,8 @@ def to_tuple(
266266 version_tuple = tuple (self .to_dict (exclude_none ).values ())
267267 return version_tuple # type: ignore
268268
269- def __str__ (self ) -> str :
270- return self .to_str ()
271-
272- def __eq__ (self , other : Any ) -> bool :
273- if isinstance (other , (dict , tuple , str )):
269+ def equals (self , other : VersionLike , * , ignore_buildmetadata : bool = False ) -> bool :
270+ if isinstance (other , (Mapping , tuple , str )):
274271 other = Version (other )
275272 # note: use self.__class__ to avoid error cause by 'pytest -v test' collect
276273 elif not isinstance (other , (Version , self .__class__ )):
@@ -281,67 +278,80 @@ def __eq__(self, other: Any) -> bool:
281278 and self .minor == other .minor
282279 and self .patch == other .patch
283280 and self .prerelease == other .prerelease
284- and self .buildmetadata == other .buildmetadata
281+ and ( ignore_buildmetadata or self .buildmetadata == other .buildmetadata )
285282 )
286283
287- def __lt__ (self , other : VersionLike ) -> bool :
288- if isinstance (other , (dict , tuple , str )):
289- other = Version (other )
290- # note: use self.__class__ to avoid error cause by 'pytest -v test' collect
291- elif not isinstance (other , (Version , self .__class__ )):
292- msg = f"Invalid argument type { type (other )} . (expected an instance of one of { (dict , tuple , str , Version )} )"
293- raise TypeError (msg )
294-
295- self_tuple = self .to_tuple (exclude_none = False )
296- other_tuple = other .to_tuple (exclude_none = False )
297-
298- for self_v , other_v in zip (self_tuple , other_tuple ):
299- if self_v == other_v :
300- continue
301- if self_v is None and other_v is not None :
302- return False
303- if self_v is not None and other_v is None :
304- return True
284+ def __str__ (self ) -> str :
285+ return self .to_str ()
305286
306- if isinstance (self_v , (int , str , NoneType )):
307- self_v = [self_v ]
308- elif not isinstance (self_v , list ):
309- raise TypeError (f"Invalid argument type { type (self_v )} ." )
310-
311- if isinstance (other_v , (int , str , NoneType )):
312- other_v = [other_v ]
313- elif not isinstance (other_v , list ):
314- raise TypeError (f"Invalid argument type { type (other_v )} ." )
315-
316- minlen = min (len (self_v ), len (other_v ))
317- if len (self_v ) != len (other_v ) and self_v [:minlen ] == other_v [:minlen ]:
318- return len (self_v ) < len (other_v )
319-
320- for self_vi , other_vi in zip (self_v , other_v ):
321- if self_vi == other_vi :
322- continue
323- if isinstance (self_vi , int ) and isinstance (other_vi , int ):
324- return self_vi < other_vi
325- if isinstance (self_vi , int ) and isinstance (other_vi , str ):
326- return True
327- if isinstance (self_vi , str ) and isinstance (other_vi , int ):
328- return False
329- if isinstance (self_vi , str ) and isinstance (other_vi , str ):
330- return self_vi < other_vi
331-
332- msg = f"Invalid attribute type { self_vi = } and { other_vi = } ."
333- raise TypeError (msg )
287+ def __eq__ (self , other : Any ) -> bool :
288+ return self .equals (other )
334289
335- return False
290+ def __lt__ (self , other : VersionLike ) -> bool :
291+ return _compare_lt (self , other )
336292
337293 def __le__ (self , other : VersionLike ) -> bool :
338294 return (self == other ) or (self < other )
339295
340296 def __gt__ (self , other : VersionLike ) -> bool :
341- return ( self != other ) and not ( self < other )
297+ return _compare_lt ( other , self )
342298
343299 def __ge__ (self , other : VersionLike ) -> bool :
344- return not (self < other )
300+ return (self == other ) or (self > other )
301+
302+
303+ def _compare_lt (
304+ x : Union [Version , Mapping , tuple , str ], y : Union [Version , Mapping , tuple , str ]
305+ ) -> bool :
306+ if isinstance (x , (Mapping , tuple , str )):
307+ x = Version (x )
308+ if isinstance (y , (Mapping , tuple , str )):
309+ y = Version (y )
310+
311+ self_tuple = x .to_tuple (exclude_none = False )
312+ other_tuple = y .to_tuple (exclude_none = False )
313+
314+ self_tuple = self_tuple [:4 ]
315+ other_tuple = other_tuple [:4 ]
316+
317+ for self_v , other_v in zip (self_tuple , other_tuple ):
318+ if self_v == other_v :
319+ continue
320+ if self_v is None and other_v is not None :
321+ return False
322+ if self_v is not None and other_v is None :
323+ return True
324+
325+ if isinstance (self_v , (int , str , NoneType )):
326+ self_v = [self_v ]
327+ elif not isinstance (self_v , list ):
328+ raise TypeError (f"Invalid argument type { type (self_v )} ." )
329+
330+ if isinstance (other_v , (int , str , NoneType )):
331+ other_v = [other_v ]
332+ elif not isinstance (other_v , list ):
333+ raise TypeError (f"Invalid argument type { type (other_v )} ." )
334+
335+ minlen = min (len (self_v ), len (other_v ))
336+ if len (self_v ) != len (other_v ) and self_v [:minlen ] == other_v [:minlen ]:
337+ return len (self_v ) < len (other_v )
338+
339+ for self_vi , other_vi in zip (self_v , other_v ):
340+ if self_vi == other_vi :
341+ continue
342+ if isinstance (self_vi , int ) and isinstance (other_vi , int ):
343+ return self_vi < other_vi
344+ if isinstance (self_vi , int ) and isinstance (other_vi , str ):
345+ return True
346+ if isinstance (self_vi , str ) and isinstance (other_vi , int ):
347+ return False
348+ if isinstance (self_vi , str ) and isinstance (other_vi , str ):
349+ return self_vi < other_vi
350+
351+ msg = f"Invalid attribute type { self_vi = } and { other_vi = } ."
352+ raise TypeError (msg )
353+
354+ return False
345355
346356
347357def _parse_version_str (version_str : str ) -> VersionDict :
0 commit comments