1+ # SPDX-License-Identifier: Apache-2.0
2+ # Nuget version utility originally from:
3+ # https://raw.githubusercontent.com/google/osv/f5647ad2f746685b08debfba0293e442f2fb9945/lib/osv/nuget.py
14# Copyright 2022 Google LLC
5+ # modified by nexB and others for integration in the Univers library
26#
3- # Licensed under the Apache License, Version 2.0 (the "License");
4- # you may not use this file except in compliance with the License.
5- # You may obtain a copy of the License at
6- #
7- # http://www.apache.org/licenses/LICENSE-2.0
8- #
9- # Unless required by applicable law or agreed to in writing, software
10- # distributed under the License is distributed on an "AS IS" BASIS,
11- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12- # See the License for the specific language governing permissions and
13- # limitations under the License.
7+ # Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
148
159import functools
1610import re
@@ -129,8 +123,13 @@ def normalize(version):
129123
130124
131125def _extract_revision (str_version ):
132- """Extract revision (4th component) from version number (if any)."""
126+ """
127+ Extract revision from ``str_version`` and return a tuple of:
128+ (dotted version string without revision, revision integer).
129+ The revision is the 4th numeric dotted component of a NuGet version.
130+ """
133131 # e.g. '1.0.0.0-prerelease'
132+ # note: each match group contains its leading dot.
134133 pattern = re .compile (r"^(\d+)(\.\d+)(\.\d+)(\.\d+)(.*)" )
135134 match = pattern .match (str_version )
136135 if not match :
@@ -142,15 +141,20 @@ def _extract_revision(str_version):
142141 )
143142
144143
144+ class InvalidNuGetVersion (Exception ):
145+ pass
146+
147+
145148@functools .total_ordering
146149class Version :
147150 """NuGet version."""
148151
149- def __init__ (self , base_semver , revision ):
152+ def __init__ (self , base_semver , revision = 0 ):
150153 self ._base_semver = base_semver
151154 if self ._base_semver .prerelease :
152155 self ._base_semver = self ._base_semver .replace (prerelease = base_semver .prerelease .lower ())
153- self ._revision = revision
156+ self ._revision = revision or 0
157+ self ._original_version = None
154158
155159 def __eq__ (self , other ):
156160 return self ._base_semver == other ._base_semver and self ._revision == other ._revision
@@ -166,6 +170,82 @@ def __lt__(self, other):
166170
167171 @classmethod
168172 def from_string (cls , str_version ):
173+ if not str_version :
174+ return
175+
176+ str_version = str_version .strip ()
177+ if " " in str_version :
178+ raise InvalidNuGetVersion (f"version cannot contain spaces: { str_version } " )
179+
180+ if not any (c .isdigit () for c in str_version ):
181+ raise InvalidNuGetVersion (f"version does not contain any digit: { str_version } " )
182+
183+ original = str_version
169184 str_version = coerce (str_version )
170185 str_version , revision = _extract_revision (str_version )
171- return Version (parse (str_version ), revision )
186+ vers = Version (base_semver = parse (str_version ), revision = revision )
187+ vers ._original_version = original
188+ return vers
189+
190+ def __repr__ (self ):
191+ return f"Version<{ self .to_string ()} >"
192+
193+ def to_string (self , with_empty_revision = False , include_prerelease = True , include_build = True ):
194+ major = self .major or "0"
195+ minor = self .minor or "0"
196+ patch = self .patch or "0"
197+
198+ if with_empty_revision :
199+ revision = self .revision and f".{ self .revision } " or ".0"
200+ else :
201+ revision = self .revision and f".{ self .revision } " or ""
202+
203+ if include_prerelease :
204+ prerelease = self .prerelease and f"-{ self .prerelease } " or ""
205+ else :
206+ prerelease = ""
207+ if include_build :
208+ build = self .build and f"+{ self .build } " or ""
209+ else :
210+ build = ""
211+
212+ return f"{ major } .{ minor } .{ patch } { revision } { prerelease } { build } "
213+
214+ def __str__ (self ):
215+ return self .to_string (
216+ with_empty_revision = False , include_prerelease = True , include_build = True
217+ )
218+
219+ @property
220+ def base_version (self ):
221+ """
222+ Return the base version dotted string composed of the four numerical
223+ segments including revision and ignoring prerelease and build.
224+ """
225+ return self .to_string (
226+ with_empty_revision = True , include_prerelease = False , include_build = False
227+ )
228+
229+ @property
230+ def major (self ):
231+ return self ._base_semver .major
232+
233+ @property
234+ def minor (self ):
235+ return self ._base_semver .minor
236+
237+ @property
238+ def patch (self ):
239+ return self ._base_semver .patch
240+
241+ @property
242+ def revision (self ):
243+ return self ._revision
244+
245+ @property
246+ def prerelease (self ):
247+ return self ._base_semver .prerelease and self ._base_semver .prerelease or ""
248+
249+ @property
250+ def build (self ):
251+ return self ._base_semver .build and self ._base_semver .build or ""
0 commit comments