Skip to content

Commit 205da7e

Browse files
authored
Merge pull request #75 from keshav-space/refactor_gem
Refactor gem and make nuget hashable
2 parents 5dc7d88 + 19de006 commit 205da7e

File tree

5 files changed

+85
-76
lines changed

5 files changed

+85
-76
lines changed

src/univers/gem.py

Lines changed: 48 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -199,19 +199,14 @@ def __init__(self, version):
199199
if not self.is_correct(version):
200200
raise InvalidVersionError(version)
201201

202-
# If version is an empty string convert it to 0
203202
version = str(version).strip()
204-
205203
self.original = version
206204

205+
# If version is an empty string convert it to 0
207206
if not version:
208207
version = "0"
209208

210209
self.version = version.replace("-", ".pre.")
211-
self._segments = ()
212-
self._canonical_segments = ()
213-
self._bump = None
214-
self._release = None
215210

216211
def __str__(self):
217212
return self.original
@@ -225,7 +220,7 @@ def equal_strictly(self, other):
225220
return self.version == other.version
226221

227222
def __hash__(self):
228-
return hash(tuple(self.canonical_segments))
223+
return hash(self.canonical_segments)
229224

230225
def __eq__(self, other):
231226
return self.canonical_segments == other.canonical_segments
@@ -242,6 +237,32 @@ def __gt__(self, other):
242237
def __ge__(self, other):
243238
return self.__cmp__(other) >= 0
244239

240+
@property
241+
def segments(self):
242+
"""
243+
Returns segments for this version where segments are
244+
ints or strings parsed from the original version string.
245+
"""
246+
segments = []
247+
find_segments = re.compile(r"[0-9]+|[a-z]+", re.IGNORECASE).findall
248+
for seg in find_segments(self.version):
249+
sub_segments = [int(seg) if seg.isdigit() else seg]
250+
segments.extend(sub_segments)
251+
return tuple(segments)
252+
253+
@property
254+
def canonical_segments(self):
255+
"""
256+
Returns "canonical segments" for this version using
257+
the Rubygems way for canonicalization.
258+
"""
259+
canonical_segments = []
260+
for segments in self.split_segments():
261+
# drop the section preceding the least significant zero (if exists)
262+
segs = list(dropwhile(lambda s: s == 0, reversed(segments)))
263+
canonical_segments.extend(reversed(segs))
264+
return tuple(canonical_segments)
265+
245266
def bump(self):
246267
"""
247268
Return a new version object where the next to the last revision number
@@ -252,40 +273,33 @@ def bump(self):
252273
>>> assert GemVersion("5.3.1").bump() == GemVersion("5.4"), repr(GemVersion("5.3.1").bump())
253274
>>> assert GemVersion("5.3.1.4-2").bump() == GemVersion("5.3.2"), GemVersion("5.3.1.4-2").bump()
254275
"""
255-
if not self._bump:
256-
segments = []
257-
for seg in self.segments:
258-
if isinstance(seg, str):
259-
break
260-
else:
261-
segments.append(seg)
262-
263-
if len(segments) > 1:
264-
segments.pop()
276+
segments = []
277+
for seg in self.segments:
278+
if isinstance(seg, str):
279+
break
280+
else:
281+
segments.append(seg)
265282

266-
segments[-1] += 1
267-
segments = [str(r) for r in segments]
268-
self._bump = GemVersion(".".join(segments))
283+
if len(segments) > 1:
284+
segments.pop()
269285

270-
return self._bump
286+
segments[-1] += 1
287+
segments = [str(r) for r in segments]
288+
return GemVersion(".".join(segments))
271289

272290
def release(self):
273291
"""
274292
Return a new GemVersion which is the release for this version (e.g.,
275293
1.2.0.a -> 1.2.0). Non-prerelease versions return themselves. A release
276294
is composed only of numeric segments.
277295
"""
278-
if not self._release:
279-
if self.prerelease():
280-
segments = self.segments
281-
while any(isinstance(s, str) for s in segments):
282-
segments.pop()
283-
segments = (str(s) for s in segments)
284-
self._release = GemVersion(".".join(segments))
285-
else:
286-
self._release = self
287-
288-
return self._release
296+
if self.prerelease():
297+
segments = list(self.segments)
298+
while any(isinstance(s, str) for s in segments):
299+
segments.pop()
300+
segments = (str(s) for s in segments)
301+
return GemVersion(".".join(segments))
302+
return self
289303

290304
def prerelease(self):
291305
"""
@@ -294,47 +308,6 @@ def prerelease(self):
294308
"""
295309
return any(not str(s).isdigit() for s in self.segments)
296310

297-
@property
298-
def segments(self):
299-
"""
300-
Return a new sequence of segments for this version where segments are
301-
ints or strings parsed from the original version string.
302-
"""
303-
if not self._segments:
304-
self._segments = self.get_segments()
305-
return list(self._segments)
306-
307-
def get_segments(self):
308-
"""
309-
Return a sequence of segments for this version where segments are ints
310-
or strings parsed from the original version string.
311-
"""
312-
find_segments = re.compile(r"[0-9]+|[a-z]+", re.IGNORECASE).findall
313-
segments = []
314-
for seg in find_segments(self.version):
315-
if seg.isdigit():
316-
seg = int(seg)
317-
segments.append(seg)
318-
return tuple(segments)
319-
320-
@property
321-
def canonical_segments(self):
322-
if not self._canonical_segments:
323-
self._canonical_segments = self.get_canonical_segments()
324-
return tuple(self._canonical_segments)
325-
326-
def get_canonical_segments(self):
327-
"""
328-
Return a new sequence of "canonical segments" for this version using
329-
the Rubygems way.
330-
"""
331-
canonical_segments = []
332-
for segments in self.split_segments():
333-
segs = list(dropwhile(lambda s: s == 0, reversed(segments)))
334-
segs = reversed(segs)
335-
canonical_segments.extend(segs)
336-
return tuple(canonical_segments)
337-
338311
def split_segments(self):
339312
"""
340313
Return a two-tuple of segments:
@@ -380,11 +353,11 @@ def __cmp__(self, other, trace=False):
380353
if self.version == other.version:
381354
return 0
382355

383-
lhsegments = self.canonical_segments
356+
lhsegments = list(self.canonical_segments)
384357
if trace:
385358
print(f" lhsegments: canonical_segments: {lhsegments!r}")
386359

387-
rhsegments = other.canonical_segments
360+
rhsegments = list(other.canonical_segments)
388361
if trace:
389362
print(f" rhsegments: canonical_segments: {rhsegments!r}")
390363

src/univers/nuget.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,14 @@ def __lt__(self, other):
175175
# Revision is the same, so ignore it for comparison purposes.
176176
return self._base_semver < other._base_semver
177177

178+
def __hash__(self):
179+
return hash(
180+
(
181+
self._base_semver.to_tuple(),
182+
self._revision,
183+
)
184+
)
185+
178186
@classmethod
179187
def from_string(cls, str_version):
180188
if not str_version:

tests/test_nuget.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import unittest
2121

22+
from univers.nuget import Version
2223
from univers.versions import NugetVersion
2324

2425

@@ -77,3 +78,13 @@ def test_less(self):
7778
self.check_order(self.assertLess, "1.0.0-pre", "1.0.0.1-alpha")
7879
self.check_order(self.assertLess, "1.0.0", "1.0.0.1-alpha")
7980
self.check_order(self.assertLess, "0.9.9.1", "1.0.0")
81+
82+
def test_NugetVersion_hash(self):
83+
vers1 = NugetVersion("1.0.1+23")
84+
vers2 = NugetVersion("1.0.1+23")
85+
assert hash(vers1) == hash(vers2)
86+
87+
def test_nuget_semver_hash(self):
88+
vers1 = Version.from_string("51.0.0+2")
89+
vers2 = Version.from_string("51.0.0+2")
90+
assert hash(vers1) == hash(vers2)

tests/test_python_semver.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#
2+
# Copyright (c) nexB Inc. and others.
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
6+
7+
from unittest import TestCase
8+
9+
import semver
10+
11+
12+
class TestPythonSemver(TestCase):
13+
def test_semver_hash(self):
14+
# python-semver doesn't consider build while hashing
15+
vers1 = semver.VersionInfo.parse("1.2.3")
16+
vers2 = semver.VersionInfo.parse("1.2.3+1")
17+
assert hash(vers1) == hash(vers2)

tests/test_rubygems_gem_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def test_segments():
173173
secondseg += 1
174174

175175
refute_version_eql("9.8.8", "9.8.7")
176-
assert GemVersion("9.8.7").segments == [9, 8, 7]
176+
assert GemVersion("9.8.7").segments == (9, 8, 7)
177177

178178

179179
def test_split_segments():

0 commit comments

Comments
 (0)