14
14
__maintainer__ = ["Sebastien Celles" , "Tom Schraitle" ]
15
15
__maintainer_email__ = "s.celles@gmail.com"
16
16
17
- _REGEX = re .compile (
18
- r"""
19
- ^
20
- (?P<major>0|[1-9]\d*)
21
- \.
22
- (?P<minor>0|[1-9]\d*)
23
- \.
24
- (?P<patch>0|[1-9]\d*)
25
- (?:-(?P<prerelease>
26
- (?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
27
- (?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*
28
- ))?
29
- (?:\+(?P<build>
30
- [0-9a-zA-Z-]+
31
- (?:\.[0-9a-zA-Z-]+)*
32
- ))?
33
- $
34
- """ ,
35
- re .VERBOSE ,
36
- )
37
17
38
18
_LAST_NUMBER = re .compile (r"(?:[^\d]*(\d+)[^\d]*)+" )
39
19
@@ -69,17 +49,7 @@ def parse(version):
69
49
>>> ver['build']
70
50
'build.4'
71
51
"""
72
- match = _REGEX .match (version )
73
- if match is None :
74
- raise ValueError ("%s is not valid SemVer string" % version )
75
-
76
- version_parts = match .groupdict ()
77
-
78
- version_parts ["major" ] = int (version_parts ["major" ])
79
- version_parts ["minor" ] = int (version_parts ["minor" ])
80
- version_parts ["patch" ] = int (version_parts ["patch" ])
81
-
82
- return version_parts
52
+ return VersionInfo .parse (version )._asdict ()
83
53
84
54
85
55
def comparator (operator ):
@@ -110,6 +80,26 @@ class VersionInfo(object):
110
80
"""
111
81
112
82
__slots__ = ("_major" , "_minor" , "_patch" , "_prerelease" , "_build" )
83
+ _REGEX = re .compile (
84
+ r"""
85
+ ^
86
+ (?P<major>0|[1-9]\d*)
87
+ \.
88
+ (?P<minor>0|[1-9]\d*)
89
+ \.
90
+ (?P<patch>0|[1-9]\d*)
91
+ (?:-(?P<prerelease>
92
+ (?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
93
+ (?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*
94
+ ))?
95
+ (?:\+(?P<build>
96
+ [0-9a-zA-Z-]+
97
+ (?:\.[0-9a-zA-Z-]+)*
98
+ ))?
99
+ $
100
+ """ ,
101
+ re .VERBOSE ,
102
+ )
113
103
114
104
def __init__ (self , major , minor = 0 , patch = 0 , prerelease = None , build = None ):
115
105
self ._major = int (major )
@@ -195,7 +185,8 @@ def bump_major(self):
195
185
>>> ver.bump_major()
196
186
VersionInfo(major=4, minor=0, patch=0, prerelease=None, build=None)
197
187
"""
198
- return parse_version_info (bump_major (str (self )))
188
+ cls = type (self )
189
+ return cls (self ._major + 1 )
199
190
200
191
def bump_minor (self ):
201
192
"""
@@ -209,7 +200,8 @@ def bump_minor(self):
209
200
>>> ver.bump_minor()
210
201
VersionInfo(major=3, minor=5, patch=0, prerelease=None, build=None)
211
202
"""
212
- return parse_version_info (bump_minor (str (self )))
203
+ cls = type (self )
204
+ return cls (self ._major , self ._minor + 1 )
213
205
214
206
def bump_patch (self ):
215
207
"""
@@ -223,7 +215,8 @@ def bump_patch(self):
223
215
>>> ver.bump_patch()
224
216
VersionInfo(major=3, minor=4, patch=6, prerelease=None, build=None)
225
217
"""
226
- return parse_version_info (bump_patch (str (self )))
218
+ cls = type (self )
219
+ return cls (self ._major , self ._minor , self ._patch + 1 )
227
220
228
221
def bump_prerelease (self , token = "rc" ):
229
222
"""
@@ -239,7 +232,11 @@ def bump_prerelease(self, token="rc"):
239
232
VersionInfo(major=3, minor=4, patch=5, prerelease='rc.2', \
240
233
build=None)
241
234
"""
242
- return parse_version_info (bump_prerelease (str (self ), token ))
235
+ cls = type (self )
236
+ prerelease = _increment_string (
237
+ self ._prerelease or (token or "rc" ) + ".0"
238
+ )
239
+ return cls (self ._major , self ._minor , self ._patch , prerelease )
243
240
244
241
def bump_build (self , token = "build" ):
245
242
"""
@@ -255,7 +252,11 @@ def bump_build(self, token="build"):
255
252
VersionInfo(major=3, minor=4, patch=5, prerelease='rc.1', \
256
253
build='build.10')
257
254
"""
258
- return parse_version_info (bump_build (str (self ), token ))
255
+ cls = type (self )
256
+ build = _increment_string (
257
+ self ._build or (token or "build" ) + ".0"
258
+ )
259
+ return cls (self ._major , self ._minor , self ._patch , self ._prerelease , build )
259
260
260
261
@comparator
261
262
def __eq__ (self , other ):
@@ -286,7 +287,7 @@ def __repr__(self):
286
287
return "%s(%s)" % (type (self ).__name__ , s )
287
288
288
289
def __str__ (self ):
289
- return format_version (* (self ._astuple ()))
290
+ return VersionInfo . format_version (* (self ._astuple ()))
290
291
291
292
def __hash__ (self ):
292
293
return hash (self ._astuple ())
@@ -304,7 +305,17 @@ def parse(version):
304
305
VersionInfo(major=3, minor=4, patch=5, \
305
306
prerelease='pre.2', build='build.4')
306
307
"""
307
- return parse_version_info (version )
308
+ match = VersionInfo ._REGEX .match (version )
309
+ if match is None :
310
+ raise ValueError ("%s is not valid SemVer string" % version )
311
+
312
+ version_parts = match .groupdict ()
313
+
314
+ version_parts ["major" ] = int (version_parts ["major" ])
315
+ version_parts ["minor" ] = int (version_parts ["minor" ])
316
+ version_parts ["patch" ] = int (version_parts ["patch" ])
317
+
318
+ return VersionInfo (** version_parts )
308
319
309
320
def replace (self , ** parts ):
310
321
"""
@@ -350,6 +361,31 @@ def isvalid(cls, version):
350
361
except ValueError :
351
362
return False
352
363
364
+ @staticmethod
365
+ def format_version (major , minor , patch , prerelease = None , build = None ):
366
+ """
367
+ Format a version according to the Semantic Versioning specification.
368
+
369
+ :param int major: the required major part of a version
370
+ :param int minor: the required minor part of a version
371
+ :param int patch: the required patch part of a version
372
+ :param str prerelease: the optional prerelease part of a version
373
+ :param str build: the optional build part of a version
374
+ :return: the formatted string
375
+ :rtype: str
376
+
377
+ >>> semver.VersionInfo.format_version(3, 4, 5, 'pre.2', 'build.4')
378
+ '3.4.5-pre.2+build.4'
379
+ """
380
+ version = "%d.%d.%d" % (major , minor , patch )
381
+ if prerelease is not None :
382
+ version = version + "-%s" % prerelease
383
+
384
+ if build is not None :
385
+ version = version + "+%s" % build
386
+
387
+ return version
388
+
353
389
354
390
def _to_dict (obj ):
355
391
if isinstance (obj , VersionInfo ):
@@ -382,16 +418,7 @@ def parse_version_info(version):
382
418
>>> version_info.build
383
419
'build.4'
384
420
"""
385
- parts = parse (version )
386
- version_info = VersionInfo (
387
- parts ["major" ],
388
- parts ["minor" ],
389
- parts ["patch" ],
390
- parts ["prerelease" ],
391
- parts ["build" ],
392
- )
393
-
394
- return version_info
421
+ return VersionInfo .parse (version )
395
422
396
423
397
424
def _nat_cmp (a , b ):
@@ -458,7 +485,8 @@ def compare(ver1, ver2):
458
485
0
459
486
"""
460
487
461
- v1 , v2 = parse (ver1 ), parse (ver2 )
488
+ v1 = VersionInfo .parse (ver1 )._asdict ()
489
+ v2 = VersionInfo .parse (ver2 )._asdict ()
462
490
463
491
return _compare_by_keys (v1 , v2 )
464
492
@@ -565,14 +593,7 @@ def format_version(major, minor, patch, prerelease=None, build=None):
565
593
>>> semver.format_version(3, 4, 5, 'pre.2', 'build.4')
566
594
'3.4.5-pre.2+build.4'
567
595
"""
568
- version = "%d.%d.%d" % (major , minor , patch )
569
- if prerelease is not None :
570
- version = version + "-%s" % prerelease
571
-
572
- if build is not None :
573
- version = version + "+%s" % build
574
-
575
- return version
596
+ return VersionInfo .format_version (major , minor , patch , prerelease , build )
576
597
577
598
578
599
def _increment_string (string ):
@@ -600,8 +621,7 @@ def bump_major(version):
600
621
>>> semver.bump_major("3.4.5")
601
622
'4.0.0'
602
623
"""
603
- verinfo = parse (version )
604
- return format_version (verinfo ["major" ] + 1 , 0 , 0 )
624
+ return str (VersionInfo .parse (version ).bump_major ())
605
625
606
626
607
627
def bump_minor (version ):
@@ -615,8 +635,7 @@ def bump_minor(version):
615
635
>>> semver.bump_minor("3.4.5")
616
636
'3.5.0'
617
637
"""
618
- verinfo = parse (version )
619
- return format_version (verinfo ["major" ], verinfo ["minor" ] + 1 , 0 )
638
+ return str (VersionInfo .parse (version ).bump_minor ())
620
639
621
640
622
641
def bump_patch (version ):
@@ -630,8 +649,7 @@ def bump_patch(version):
630
649
>>> semver.bump_patch("3.4.5")
631
650
'3.4.6'
632
651
"""
633
- verinfo = parse (version )
634
- return format_version (verinfo ["major" ], verinfo ["minor" ], verinfo ["patch" ] + 1 )
652
+ return str (VersionInfo .parse (version ).bump_patch ())
635
653
636
654
637
655
def bump_prerelease (version , token = "rc" ):
@@ -646,13 +664,7 @@ def bump_prerelease(version, token="rc"):
646
664
>>> semver.bump_prerelease('3.4.5', 'dev')
647
665
'3.4.5-dev.1'
648
666
"""
649
- verinfo = parse (version )
650
- verinfo ["prerelease" ] = _increment_string (
651
- verinfo ["prerelease" ] or (token or "rc" ) + ".0"
652
- )
653
- return format_version (
654
- verinfo ["major" ], verinfo ["minor" ], verinfo ["patch" ], verinfo ["prerelease" ]
655
- )
667
+ return str (VersionInfo .parse (version ).bump_prerelease (token ))
656
668
657
669
658
670
def bump_build (version , token = "build" ):
@@ -667,15 +679,7 @@ def bump_build(version, token="build"):
667
679
>>> semver.bump_build('3.4.5-rc.1+build.9')
668
680
'3.4.5-rc.1+build.10'
669
681
"""
670
- verinfo = parse (version )
671
- verinfo ["build" ] = _increment_string (verinfo ["build" ] or (token or "build" ) + ".0" )
672
- return format_version (
673
- verinfo ["major" ],
674
- verinfo ["minor" ],
675
- verinfo ["patch" ],
676
- verinfo ["prerelease" ],
677
- verinfo ["build" ],
678
- )
682
+ return str (VersionInfo .parse (version ).bump_build (token ))
679
683
680
684
681
685
def finalize_version (version ):
@@ -692,8 +696,26 @@ def finalize_version(version):
692
696
>>> semver.finalize_version('1.2.3-rc.5')
693
697
'1.2.3'
694
698
"""
695
- verinfo = parse (version )
696
- return format_version (verinfo ["major" ], verinfo ["minor" ], verinfo ["patch" ])
699
+ verinfo = VersionInfo .parse (version )
700
+ return VersionInfo .format_version (verinfo .major , verinfo .minor , verinfo .patch )
701
+
702
+
703
+ def replace (version , ** parts ):
704
+ """
705
+ Replace one or more parts of a version and return the new string.
706
+
707
+ :param str version: the version string to replace
708
+ :param dict parts: the parts to be updated. Valid keys are:
709
+ ``major``, ``minor``, ``patch``, ``prerelease``, or ``build``
710
+ :return: the replaced version string
711
+ :raises: TypeError, if ``parts`` contains invalid keys
712
+ :rtype: str
713
+
714
+ >>> import semver
715
+ >>> semver.replace("1.2.3", major=2, patch=10)
716
+ '2.2.10'
717
+ """
718
+ return str (VersionInfo .parse (version ).replace (** parts ))
697
719
698
720
699
721
def cmd_bump (args ):
@@ -719,7 +741,7 @@ def cmd_bump(args):
719
741
# print the help and exit
720
742
args .parser .parse_args (["bump" , "-h" ])
721
743
722
- ver = parse_version_info (args .version )
744
+ ver = VersionInfo . parse (args .version )
723
745
# get the respective method and call it
724
746
func = getattr (ver , maptable [args .bump ])
725
747
return str (func ())
0 commit comments