Skip to content

Commit cc62f16

Browse files
committed
updated methods in semver class
1 parent 07ca6e0 commit cc62f16

File tree

3 files changed

+54
-65
lines changed

3 files changed

+54
-65
lines changed

splitio/models/grammar/matchers/semver.py

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66

77
_LOGGER = logging.getLogger(__name__)
88

9+
def build_semver_or_none(version):
10+
try:
11+
return Semver(version)
12+
except (RuntimeError, ValueError):
13+
_LOGGER.error("Invalid semver version: %s", version)
14+
return None
15+
916
class Semver(object):
1017
"""Semver class."""
1118

@@ -29,38 +36,29 @@ def __init__(self, version):
2936
self._metadata = ""
3037
self._parse(version)
3138

32-
@classmethod
33-
def build(cls, version):
34-
try:
35-
self = cls(version)
36-
except RuntimeError as e:
37-
_LOGGER.error("Failed to parse Semver data, incorrect data type: %s", e)
38-
return None
39-
40-
return self
4139

4240
def _parse(self, version):
4341
"""
4442
Parse the string in self.version to update the other internal variables
4543
"""
46-
without_metadata = self._remove_metadata_if_exists(version)
44+
without_metadata = self._extract_metadata(version)
4745
index = without_metadata.find(self._PRE_RELEASE_DELIMITER)
4846
if index == -1:
4947
self._is_stable = True
5048
else:
5149
pre_release_data = without_metadata[index+1:]
5250
if pre_release_data == "":
53-
raise RuntimeError("Pre-release is empty despite delimeter exists: " + version)
51+
raise RuntimeError("Pre-release is empty despite delimiter exists: " + version)
5452

5553
without_metadata = without_metadata[:index]
5654
for pre_digit in pre_release_data.split(self._VALUE_DELIMITER):
5755
if pre_digit.isnumeric():
5856
pre_digit = str(int(pre_digit))
5957
self._pre_release.append(pre_digit)
6058

61-
self._set_major_minor_and_patch(without_metadata)
59+
self._set_components(without_metadata)
6260

63-
def _remove_metadata_if_exists(self, version):
61+
def _extract_metadata(self, version):
6462
"""
6563
Check if there is any metadata characters in self.version.
6664
@@ -73,11 +71,11 @@ def _remove_metadata_if_exists(self, version):
7371

7472
self._metadata = version[index+1:]
7573
if self._metadata == "":
76-
raise RuntimeError("Metadata is empty despite delimeter exists: " + version)
74+
raise RuntimeError("Metadata is empty despite delimiter exists: " + version)
7775

7876
return version[:index]
7977

80-
def _set_major_minor_and_patch(self, version):
78+
def _set_components(self, version):
8179
"""
8280
Set the major, minor and patch internal variables based on string passed.
8381
@@ -140,7 +138,7 @@ def compare(self, to_compare):
140138
continue
141139

142140
if self._pre_release[i].isnumeric() and to_compare._pre_release[i].isnumeric():
143-
return self._compare_vars(int(self._pre_release[i]), int(to_compare._pre_release[i]))
141+
return self._compare_vars(int(self._pre_release[i]), int(to_compare._pre_release[i]))
144142

145143
return self._compare_vars(self._pre_release[i], to_compare._pre_release[i])
146144

@@ -179,7 +177,7 @@ def _build(self, raw_matcher):
179177
:type raw_matcher: dict
180178
"""
181179
self._data = raw_matcher.get('stringMatcherData')
182-
self._semver = Semver.build(self._data)
180+
self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
183181

184182
def _match(self, key, attributes=None, context=None):
185183
"""
@@ -195,15 +193,15 @@ def _match(self, key, attributes=None, context=None):
195193
:returns: Wheter the match is successful.
196194
:rtype: bool
197195
"""
198-
if self._data is None or self._semver is None:
196+
if self._semver is None:
199197
_LOGGER.error("stringMatcherData is required for EQUAL_TO_SEMVER matcher type")
200198
return False
201199

202200
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
203201
if matching_data is None:
204202
return False
205203

206-
matching_semver = Semver.build(matching_data)
204+
matching_semver = build_semver_or_none(matching_data)
207205
if matching_semver is None:
208206
return False
209207

@@ -228,7 +226,7 @@ def _build(self, raw_matcher):
228226
:type raw_matcher: dict
229227
"""
230228
self._data = raw_matcher.get('stringMatcherData')
231-
self._semver = Semver.build(self._data)
229+
self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
232230

233231
def _match(self, key, attributes=None, context=None):
234232
"""
@@ -244,15 +242,15 @@ def _match(self, key, attributes=None, context=None):
244242
:returns: Wheter the match is successful.
245243
:rtype: bool
246244
"""
247-
if self._data is None or self._semver is None:
245+
if self._semver is None:
248246
_LOGGER.error("stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type")
249247
return False
250248

251249
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
252250
if matching_data is None:
253251
return False
254252

255-
matching_semver = Semver.build(matching_data)
253+
matching_semver = build_semver_or_none(matching_data)
256254
if matching_semver is None:
257255
return False
258256

@@ -277,7 +275,7 @@ def _build(self, raw_matcher):
277275
:type raw_matcher: dict
278276
"""
279277
self._data = raw_matcher.get('stringMatcherData')
280-
self._semver = Semver.build(self._data)
278+
self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
281279

282280
def _match(self, key, attributes=None, context=None):
283281
"""
@@ -293,15 +291,15 @@ def _match(self, key, attributes=None, context=None):
293291
:returns: Wheter the match is successful.
294292
:rtype: bool
295293
"""
296-
if self._data is None or self._semver is None:
294+
if self._semver is None:
297295
_LOGGER.error("stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type")
298296
return False
299297

300298
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
301299
if matching_data is None:
302300
return False
303301

304-
matching_semver = Semver.build(matching_data)
302+
matching_semver = build_semver_or_none(matching_data)
305303
if matching_semver is None:
306304
return False
307305

@@ -326,8 +324,8 @@ def _build(self, raw_matcher):
326324
:type raw_matcher: dict
327325
"""
328326
self._data = raw_matcher.get('betweenStringMatcherData')
329-
self._semver_start = Semver.build(self._data['start']) if self._data.get('start') is not None else None
330-
self._semver_end = Semver.build(self._data['end']) if self._data.get('end') is not None else None
327+
self._semver_start = build_semver_or_none(self._data['start'])
328+
self._semver_end = build_semver_or_none(self._data['end'])
331329

332330
def _match(self, key, attributes=None, context=None):
333331
"""
@@ -343,15 +341,15 @@ def _match(self, key, attributes=None, context=None):
343341
:returns: Wheter the match is successful.
344342
:rtype: bool
345343
"""
346-
if self._data is None or self._semver_start is None or self._semver_end is None:
344+
if self._semver_start is None or self._semver_end is None:
347345
_LOGGER.error("betweenStringMatcherData is required for BETWEEN_SEMVER matcher type")
348346
return False
349347

350348
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
351349
if matching_data is None:
352350
return False
353351

354-
matching_semver = Semver.build(matching_data)
352+
matching_semver = build_semver_or_none(matching_data)
355353
if matching_semver is None:
356354
return False
357355

@@ -375,11 +373,9 @@ def _build(self, raw_matcher):
375373
:param raw_matcher: raw matcher as fetched from splitChanges response.
376374
:type raw_matcher: dict
377375
"""
378-
self._data = raw_matcher.get('whitelistMatcherData')
379-
if self._data is not None:
380-
self._data = self._data.get('whitelist')
381-
382-
self._semver_list = [Semver.build(item) if item is not None else None for item in self._data] if self._data is not None else []
376+
self._data = raw_matcher['whitelistMatcherData']['whitelist']
377+
semver_list = [build_semver_or_none(item) if item is not None else None for item in self._data]
378+
self._semver_list = frozenset([item.version for item in semver_list])
383379

384380
def _match(self, key, attributes=None, context=None):
385381
"""
@@ -395,19 +391,19 @@ def _match(self, key, attributes=None, context=None):
395391
:returns: Wheter the match is successful.
396392
:rtype: bool
397393
"""
398-
if self._data is None:
394+
if self._semver_list is None:
399395
_LOGGER.error("whitelistMatcherData is required for IN_LIST_SEMVER matcher type")
400396
return False
401397

402398
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
403399
if matching_data is None:
404400
return False
405401

406-
matching_semver = Semver.build(matching_data)
402+
matching_semver = build_semver_or_none(matching_data)
407403
if matching_semver is None:
408404
return False
409405

410-
return any([item.version == matching_semver.version if item is not None else False for item in self._semver_list])
406+
return matching_semver.version in self._semver_list
411407

412408
def __str__(self):
413409
"""Return string Representation."""

tests/models/grammar/test_matchers.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ def test_from_raw(self, mocker):
899899
"""Test parsing from raw json/dict."""
900900
parsed = matchers.from_raw(self.raw)
901901
assert isinstance(parsed, matchers.EqualToSemverMatcher)
902-
assert parsed._data == "2.1.8"
902+
assert parsed._semver.version == "2.1.8"
903903
assert isinstance(parsed._semver, Semver)
904904
assert parsed._semver._major == 2
905905
assert parsed._semver._minor == 1
@@ -940,7 +940,7 @@ def test_from_raw(self, mocker):
940940
"""Test parsing from raw json/dict."""
941941
parsed = matchers.from_raw(self.raw)
942942
assert isinstance(parsed, matchers.GreaterThanOrEqualToSemverMatcher)
943-
assert parsed._data == "2.1.8"
943+
assert parsed._semver.version == "2.1.8"
944944
assert isinstance(parsed._semver, Semver)
945945
assert parsed._semver._major == 2
946946
assert parsed._semver._minor == 1
@@ -982,7 +982,7 @@ def test_from_raw(self, mocker):
982982
"""Test parsing from raw json/dict."""
983983
parsed = matchers.from_raw(self.raw)
984984
assert isinstance(parsed, matchers.LessThanOrEqualToSemverMatcher)
985-
assert parsed._data == "2.1.8"
985+
assert parsed._semver.version == "2.1.8"
986986
assert isinstance(parsed._semver, Semver)
987987
assert parsed._semver._major == 2
988988
assert parsed._semver._minor == 1
@@ -1024,14 +1024,15 @@ def test_from_raw(self, mocker):
10241024
"""Test parsing from raw json/dict."""
10251025
parsed = matchers.from_raw(self.raw)
10261026
assert isinstance(parsed, matchers.BetweenSemverMatcher)
1027-
assert parsed._data == {"start": "2.1.8", "end": "2.1.11"}
10281027
assert isinstance(parsed._semver_start, Semver)
10291028
assert isinstance(parsed._semver_end, Semver)
1029+
assert parsed._semver_start.version == "2.1.8"
10301030
assert parsed._semver_start._major == 2
10311031
assert parsed._semver_start._minor == 1
10321032
assert parsed._semver_start._patch == 8
10331033
assert parsed._semver_start._pre_release == []
10341034

1035+
assert parsed._semver_end.version == "2.1.11"
10351036
assert parsed._semver_end._major == 2
10361037
assert parsed._semver_end._minor == 1
10371038
assert parsed._semver_end._patch == 11
@@ -1073,16 +1074,9 @@ def test_from_raw(self, mocker):
10731074
parsed = matchers.from_raw(self.raw)
10741075
assert isinstance(parsed, matchers.InListSemverMatcher)
10751076
assert parsed._data == ["2.1.8", "2.1.11"]
1076-
assert [isinstance(item, Semver) for item in parsed._semver_list]
1077-
assert parsed._semver_list[0]._major == 2
1078-
assert parsed._semver_list[0]._minor == 1
1079-
assert parsed._semver_list[0]._patch == 8
1080-
assert parsed._semver_list[0]._pre_release == []
1081-
1082-
assert parsed._semver_list[1]._major == 2
1083-
assert parsed._semver_list[1]._minor == 1
1084-
assert parsed._semver_list[1]._patch == 11
1085-
assert parsed._semver_list[1]._pre_release == []
1077+
assert [isinstance(item, str) for item in parsed._semver_list]
1078+
assert "2.1.8" in parsed._semver_list
1079+
assert "2.1.11" in parsed._semver_list
10861080

10871081
def test_matcher_behaviour(self, mocker):
10881082
"""Test if the matcher works properly."""
Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
"""Condition model tests module."""
2-
import pytest
32
import csv
43
import os
54

6-
from splitio.models.grammar.matchers.semver import Semver
5+
from splitio.models.grammar.matchers.semver import build_semver_or_none
76

87
valid_versions = os.path.join(os.path.dirname(__file__), 'files', 'valid-semantic-versions.csv')
98
invalid_versions = os.path.join(os.path.dirname(__file__), 'files', 'invalid-semantic-versions.csv')
@@ -17,27 +16,27 @@ def test_valid_versions(self):
1716
with open(valid_versions) as csvfile:
1817
reader = csv.DictReader(csvfile)
1918
for row in reader:
20-
assert Semver.build(row['higher']) is not None
21-
assert Semver.build(row['lower']) is not None
19+
assert build_semver_or_none(row['higher']) is not None
20+
assert build_semver_or_none(row['lower']) is not None
2221

2322
def test_invalid_versions(self):
2423
with open(invalid_versions) as csvfile:
2524
reader = csv.DictReader(csvfile)
2625
for row in reader:
27-
assert Semver.build(row['invalid']) is None
26+
assert build_semver_or_none(row['invalid']) is None
2827

2928
def test_compare(self):
3029
with open(valid_versions) as csvfile:
3130
reader = csv.DictReader(csvfile)
3231
for row in reader:
33-
assert Semver.build(row['higher']).compare(Semver.build(row['lower'])) == 1
34-
assert Semver.build(row['lower']).compare(Semver.build(row['higher'])) == -1
32+
assert build_semver_or_none(row['higher']).compare(build_semver_or_none(row['lower'])) == 1
33+
assert build_semver_or_none(row['lower']).compare(build_semver_or_none(row['higher'])) == -1
3534

3635
with open(equalto_versions) as csvfile:
3736
reader = csv.DictReader(csvfile)
3837
for row in reader:
39-
version1 = Semver.build(row['version1'])
40-
version2 = Semver.build(row['version2'])
38+
version1 = build_semver_or_none(row['version1'])
39+
version2 = build_semver_or_none(row['version2'])
4140
if row['equals'] == "true":
4241
assert version1.version == version2.version
4342
else:
@@ -46,14 +45,14 @@ def test_compare(self):
4645
with open(between_versions) as csvfile:
4746
reader = csv.DictReader(csvfile)
4847
for row in reader:
49-
version1 = Semver.build(row['version1'])
50-
version2 = Semver.build(row['version2'])
51-
version3 = Semver.build(row['version3'])
48+
version1 = build_semver_or_none(row['version1'])
49+
version2 = build_semver_or_none(row['version2'])
50+
version3 = build_semver_or_none(row['version3'])
5251
if row['expected'] == "true":
5352
assert version2.compare(version1) >= 0 and version3.compare(version2) >= 0
5453
else:
5554
assert version2.compare(version1) < 0 or version3.compare(version2) < 0
5655

5756
def test_leading_zeros(self):
58-
assert Semver.build('1.01.2').version == '1.1.2'
59-
assert Semver.build('1.01.2-rc.01').version == '1.1.2-rc.1'
57+
assert build_semver_or_none('1.01.2').version == '1.1.2'
58+
assert build_semver_or_none('1.01.2-rc.01').version == '1.1.2-rc.1'

0 commit comments

Comments
 (0)