4
4
5
5
import collections
6
6
import re
7
- import sys
8
7
9
8
10
9
__version__ = '2.7.6'
@@ -59,26 +58,59 @@ def parse(version):
59
58
return version_parts
60
59
61
60
62
- VersionInfo = collections .namedtuple (
63
- 'VersionInfo' , 'major minor patch prerelease build' )
64
-
65
- # Only change it for Python > 3 as it is readonly
66
- # for version 2
67
- if sys .version_info >= (3 , 5 ):
68
- VersionInfo .__doc__ = """
69
- :param int major: version when you make incompatible API changes.
70
- :param int minor: version when you add functionality in
71
- a backwards-compatible manner.
72
- :param int patch: version when you make backwards-compatible bug fixes.
73
- :param str prerelease: an optional prerelease string
74
- :param str build: an optional build string
75
-
76
- >>> import semver
77
- >>> ver = semver.parse('3.4.5-pre.2+build.4')
78
- >>> ver
79
- {'build': 'build.4', 'major': 3, 'minor': 4, 'patch': 5,
80
- 'prerelease': 'pre.2'}
81
- """
61
+ class VersionInfo (collections .namedtuple (
62
+ 'VersionInfo' , 'major minor patch prerelease build' )):
63
+ """
64
+ :param int major: version when you make incompatible API changes.
65
+ :param int minor: version when you add functionality in
66
+ a backwards-compatible manner.
67
+ :param int patch: version when you make backwards-compatible bug fixes.
68
+ :param str prerelease: an optional prerelease string
69
+ :param str build: an optional build string
70
+
71
+ >>> import semver
72
+ >>> ver = semver.parse('3.4.5-pre.2+build.4')
73
+ >>> ver
74
+ {'build': 'build.4', 'major': 3, 'minor': 4, 'patch': 5,
75
+ 'prerelease': 'pre.2'}
76
+ """
77
+ __slots__ = ()
78
+
79
+ def __eq__ (self , other ):
80
+ if not isinstance (other , (VersionInfo , dict )):
81
+ return NotImplemented
82
+ return _compare_by_keys (self ._asdict (), _to_dict (other )) == 0
83
+
84
+ def __ne__ (self , other ):
85
+ if not isinstance (other , (VersionInfo , dict )):
86
+ return NotImplemented
87
+ return _compare_by_keys (self ._asdict (), _to_dict (other )) != 0
88
+
89
+ def __lt__ (self , other ):
90
+ if not isinstance (other , (VersionInfo , dict )):
91
+ return NotImplemented
92
+ return _compare_by_keys (self ._asdict (), _to_dict (other )) < 0
93
+
94
+ def __le__ (self , other ):
95
+ if not isinstance (other , (VersionInfo , dict )):
96
+ return NotImplemented
97
+ return _compare_by_keys (self ._asdict (), _to_dict (other )) <= 0
98
+
99
+ def __gt__ (self , other ):
100
+ if not isinstance (other , (VersionInfo , dict )):
101
+ return NotImplemented
102
+ return _compare_by_keys (self ._asdict (), _to_dict (other )) > 0
103
+
104
+ def __ge__ (self , other ):
105
+ if not isinstance (other , (VersionInfo , dict )):
106
+ return NotImplemented
107
+ return _compare_by_keys (self ._asdict (), _to_dict (other )) >= 0
108
+
109
+
110
+ def _to_dict (obj ):
111
+ if isinstance (obj , VersionInfo ):
112
+ return obj ._asdict ()
113
+ return obj
82
114
83
115
84
116
def parse_version_info (version ):
@@ -96,6 +128,52 @@ def parse_version_info(version):
96
128
return version_info
97
129
98
130
131
+ def _nat_cmp (a , b ):
132
+ def convert (text ):
133
+ return int (text ) if re .match ('[0-9]+' , text ) else text
134
+
135
+ def split_key (key ):
136
+ return [convert (c ) for c in key .split ('.' )]
137
+
138
+ def cmp_prerelease_tag (a , b ):
139
+ if isinstance (a , int ) and isinstance (b , int ):
140
+ return cmp (a , b )
141
+ elif isinstance (a , int ):
142
+ return - 1
143
+ elif isinstance (b , int ):
144
+ return 1
145
+ else :
146
+ return cmp (a , b )
147
+
148
+ a , b = a or '' , b or ''
149
+ a_parts , b_parts = split_key (a ), split_key (b )
150
+ for sub_a , sub_b in zip (a_parts , b_parts ):
151
+ cmp_result = cmp_prerelease_tag (sub_a , sub_b )
152
+ if cmp_result != 0 :
153
+ return cmp_result
154
+ else :
155
+ return cmp (len (a ), len (b ))
156
+
157
+
158
+ def _compare_by_keys (d1 , d2 ):
159
+ for key in ['major' , 'minor' , 'patch' ]:
160
+ v = cmp (d1 .get (key ), d2 .get (key ))
161
+ if v :
162
+ return v
163
+
164
+ rc1 , rc2 = d1 .get ('prerelease' ), d2 .get ('prerelease' )
165
+ rccmp = _nat_cmp (rc1 , rc2 )
166
+
167
+ if not rccmp :
168
+ return 0
169
+ if not rc1 :
170
+ return 1
171
+ elif not rc2 :
172
+ return - 1
173
+
174
+ return rccmp
175
+
176
+
99
177
def compare (ver1 , ver2 ):
100
178
"""Compare two versions
101
179
@@ -105,53 +183,10 @@ def compare(ver1, ver2):
105
183
zero if ver1 == ver2 and strictly positive if ver1 > ver2
106
184
:rtype: int
107
185
"""
108
- def nat_cmp (a , b ):
109
- def convert (text ):
110
- return int (text ) if re .match ('[0-9]+' , text ) else text
111
-
112
- def split_key (key ):
113
- return [convert (c ) for c in key .split ('.' )]
114
-
115
- def cmp_prerelease_tag (a , b ):
116
- if isinstance (a , int ) and isinstance (b , int ):
117
- return cmp (a , b )
118
- elif isinstance (a , int ):
119
- return - 1
120
- elif isinstance (b , int ):
121
- return 1
122
- else :
123
- return cmp (a , b )
124
-
125
- a , b = a or '' , b or ''
126
- a_parts , b_parts = split_key (a ), split_key (b )
127
- for sub_a , sub_b in zip (a_parts , b_parts ):
128
- cmp_result = cmp_prerelease_tag (sub_a , sub_b )
129
- if cmp_result != 0 :
130
- return cmp_result
131
- else :
132
- return cmp (len (a ), len (b ))
133
-
134
- def compare_by_keys (d1 , d2 ):
135
- for key in ['major' , 'minor' , 'patch' ]:
136
- v = cmp (d1 .get (key ), d2 .get (key ))
137
- if v :
138
- return v
139
-
140
- rc1 , rc2 = d1 .get ('prerelease' ), d2 .get ('prerelease' )
141
- rccmp = nat_cmp (rc1 , rc2 )
142
-
143
- if not rccmp :
144
- return 0
145
- if not rc1 :
146
- return 1
147
- elif not rc2 :
148
- return - 1
149
-
150
- return rccmp
151
186
152
187
v1 , v2 = parse (ver1 ), parse (ver2 )
153
188
154
- return compare_by_keys (v1 , v2 )
189
+ return _compare_by_keys (v1 , v2 )
155
190
156
191
157
192
def match (version , match_expr ):
0 commit comments