@@ -68,15 +68,19 @@ class Version:
68
68
__slots__ = ("_major" , "_minor" , "_patch" , "_prerelease" , "_build" )
69
69
#: Regex for number in a prerelease
70
70
_LAST_NUMBER = re .compile (r"(?:[^\d]*(\d+)[^\d]*)+" )
71
- #: Regex for a semver version
72
- _REGEX = re . compile (
71
+ #: Regex template for a semver version
72
+ _REGEX_TEMPLATE = \
73
73
r"""
74
74
^
75
75
(?P<major>0|[1-9]\d*)
76
- \.
77
- (?P<minor>0|[1-9]\d*)
78
- \.
79
- (?P<patch>0|[1-9]\d*)
76
+ (?:
77
+ \.
78
+ (?P<minor>0|[1-9]\d*)
79
+ (?:
80
+ \.
81
+ (?P<patch>0|[1-9]\d*)
82
+ ){opt_patch}
83
+ ){opt_minor}
80
84
(?:-(?P<prerelease>
81
85
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
82
86
(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*
@@ -86,7 +90,15 @@ class Version:
86
90
(?:\.[0-9a-zA-Z-]+)*
87
91
))?
88
92
$
89
- """ ,
93
+ """
94
+ #: Regex for a semver version
95
+ _REGEX = re .compile (
96
+ _REGEX_TEMPLATE .format (opt_patch = '' , opt_minor = '' ),
97
+ re .VERBOSE ,
98
+ )
99
+ #: Regex for a semver version that might be shorter
100
+ _REGEX_OPTIONAL_MINOR_AND_PATCH = re .compile (
101
+ _REGEX_TEMPLATE .format (opt_patch = '?' , opt_minor = '?' ),
90
102
re .VERBOSE ,
91
103
)
92
104
@@ -553,15 +565,26 @@ def match(self, match_expr: str) -> bool:
553
565
return cmp_res in possibilities
554
566
555
567
@classmethod
556
- def parse (cls , version : String ) -> "Version" :
568
+ def parse (
569
+ cls ,
570
+ version : String ,
571
+ optional_minor_and_patch : bool = False
572
+ ) -> "Version" :
557
573
"""
558
574
Parse version string to a Version instance.
559
575
560
576
.. versionchanged:: 2.11.0
561
577
Changed method from static to classmethod to
562
578
allow subclasses.
579
+ .. versionchanged:: 3.0.0
580
+ Added optional parameter optional_minor_and_patch to allow optional
581
+ minor and patch parts.
563
582
564
583
:param version: version string
584
+ :param optional_minor_and_patch: if set to true, the version string to parse \
585
+ can contain optional minor and patch parts. Optional parts are set to zero.
586
+ By default (False), the version string to parse has to follow the semver
587
+ specification.
565
588
:return: a new :class:`Version` instance
566
589
:raises ValueError: if version is invalid
567
590
:raises TypeError: if version contains the wrong type
@@ -575,11 +598,18 @@ def parse(cls, version: String) -> "Version":
575
598
elif not isinstance (version , String .__args__ ): # type: ignore
576
599
raise TypeError ("not expecting type '%s'" % type (version ))
577
600
578
- match = cls ._REGEX .match (version )
601
+ if optional_minor_and_patch :
602
+ match = cls ._REGEX_OPTIONAL_MINOR_AND_PATCH .match (version )
603
+ else :
604
+ match = cls ._REGEX .match (version )
579
605
if match is None :
580
606
raise ValueError (f"{ version } is not valid SemVer string" )
581
607
582
608
matched_version_parts : Dict [str , Any ] = match .groupdict ()
609
+ if not matched_version_parts ['minor' ]:
610
+ matched_version_parts ['minor' ] = 0
611
+ if not matched_version_parts ['patch' ]:
612
+ matched_version_parts ['patch' ] = 0
583
613
584
614
return cls (** matched_version_parts )
585
615
0 commit comments