Skip to content

Commit c92489a

Browse files
committed
Added support for the from_native function for both Composer and Golang version ranges. issue #47
Signed-off-by: Aayush Kumar <[email protected]>
1 parent 9c15915 commit c92489a

File tree

3 files changed

+319
-3
lines changed

3 files changed

+319
-3
lines changed

src/univers/version_range.py

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -842,8 +842,11 @@ class NugetVersionRange(MavenVersionRange):
842842

843843

844844
class ComposerVersionRange(VersionRange):
845-
# TODO composer may need its own scheme see https//github.com/aboutcode-org/univers/issues/5
846-
# and https//getcomposer.org/doc/articles/versions.md
845+
"""
846+
Composer version range as documented at
847+
https://getcomposer.org/doc/articles/versions.md
848+
"""
849+
847850
scheme = "composer"
848851
version_class = versions.ComposerVersion
849852

@@ -856,6 +859,75 @@ class ComposerVersionRange(VersionRange):
856859
"=": "=", # This is not a native composer-semver comparator, but is used in the gitlab version range for composer packages.
857860
}
858861

862+
@classmethod
863+
def from_native(cls, string):
864+
"""
865+
Parse a Composer version range string into a version range object.
866+
"""
867+
string = string.strip()
868+
869+
if string.startswith("^"):
870+
base_version = string[1:]
871+
base_version_obj = cls.version_class(base_version)
872+
base_parts = base_version.split(".")
873+
if base_parts[0] == "0":
874+
upper_constraint = VersionConstraint(
875+
comparator="<", version=cls.version_class(f"0.{int(base_parts[1]) + 1}.0")
876+
)
877+
else:
878+
upper_constraint = VersionConstraint(
879+
comparator="<", version=cls.version_class(f"{int(base_parts[0]) + 1}.0.0")
880+
)
881+
lower_constraint = VersionConstraint(comparator=">=", version=base_version_obj)
882+
return cls(constraints=[lower_constraint, upper_constraint])
883+
884+
if string.startswith("~"):
885+
base_version = string[1:]
886+
base_version_obj = cls.version_class(base_version)
887+
base_parts = base_version.split(".")
888+
889+
if len(base_parts) == 3:
890+
upper_constraint = VersionConstraint(
891+
comparator="<",
892+
version=cls.version_class(f"{base_parts[0]}.{int(base_parts[1]) + 1}.0"),
893+
)
894+
else:
895+
upper_constraint = VersionConstraint(
896+
comparator="<", version=cls.version_class(f"{int(base_parts[0]) + 1}.0.0")
897+
)
898+
899+
lower_constraint = VersionConstraint(comparator=">=", version=base_version_obj)
900+
return cls(constraints=[lower_constraint, upper_constraint])
901+
902+
if ".*" in string:
903+
base_version = string.replace(".*", ".0")
904+
base_version_obj = cls.version_class(base_version)
905+
base_parts = base_version.split(".")
906+
upper_constraint = VersionConstraint(
907+
comparator="<",
908+
version=cls.version_class(f"{base_parts[0]}.{int(base_parts[1]) + 1}.0"),
909+
)
910+
lower_constraint = VersionConstraint(comparator=">=", version=base_version_obj)
911+
return cls(constraints=[lower_constraint, upper_constraint])
912+
913+
constraints = []
914+
915+
segments = string.split("||")
916+
917+
for segment in segments:
918+
if not any(op in string for op in cls.vers_by_native_comparators):
919+
segment = "==" + segment
920+
specifiers = SpecifierSet(segment)
921+
for spec in specifiers:
922+
operator = spec.operator
923+
version = spec.version
924+
version = cls.version_class(version)
925+
comparator = cls.vers_by_native_comparators.get(operator, "=")
926+
constraint = VersionConstraint(comparator=comparator, version=version)
927+
constraints.append(constraint)
928+
929+
return cls(constraints=constraints)
930+
859931

860932
class RpmVersionRange(VersionRange):
861933
# http://ftp.rpm.org/api/4.4.2.2/dependencies.html
@@ -942,7 +1014,7 @@ def from_natives(cls, strings):
9421014

9431015
class GolangVersionRange(VersionRange):
9441016
"""
945-
Go modules use strict semver with pseudo numbering for Git repos
1017+
Go modules use strict semver with pseudo numbering for Git commits.
9461018
https://go.dev/doc/modules/version-numbers
9471019
"""
9481020

@@ -958,6 +1030,39 @@ class GolangVersionRange(VersionRange):
9581030
"=": "=", # This is not a native golang-semver comparator, but is used in the gitlab version range for go packages.
9591031
}
9601032

1033+
@classmethod
1034+
def from_native(cls, string):
1035+
"""
1036+
Parse a native GoLang version range into a set of constraints.
1037+
"""
1038+
constraints = []
1039+
1040+
segments = string.split("||")
1041+
for segment in segments:
1042+
1043+
if not any(op in string for op in cls.vers_by_native_comparators):
1044+
segment = "==" + segment
1045+
1046+
specifiers = SpecifierSet(segment)
1047+
for spec in specifiers:
1048+
operator = spec.operator
1049+
version = spec.version
1050+
version = cls.version_class(version)
1051+
comparator = cls.vers_by_native_comparators.get(operator, "=")
1052+
constraint = VersionConstraint(comparator=comparator, version=version)
1053+
constraints.append(constraint)
1054+
1055+
return cls(constraints=constraints)
1056+
1057+
@classmethod
1058+
def from_natives(cls, strings):
1059+
if isinstance(strings, str):
1060+
return cls.from_native(strings)
1061+
constraints = []
1062+
for rel in strings:
1063+
constraints.extend(cls.from_native(rel).constraints)
1064+
return cls(constraints=constraints)
1065+
9611066

9621067
class GenericVersionRange(VersionRange):
9631068
scheme = "generic"

tests/test_composer_version_range.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Copyright (c) nexB Inc. and others.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Visit https://aboutcode.org and https://github.com/aboutcode-org/univers for support and download.
5+
6+
from univers.version_constraint import VersionConstraint
7+
from univers.version_range import ComposerVersionRange
8+
from univers.versions import ComposerVersion
9+
10+
11+
def test_composer_exact_version():
12+
version_range = ComposerVersionRange.from_native("1.3.2")
13+
assert version_range == ComposerVersionRange(
14+
constraints=(VersionConstraint(comparator="=", version=ComposerVersion(string="1.3.2")),)
15+
)
16+
17+
18+
def test_composer_greater_than_or_equal():
19+
version_range = ComposerVersionRange.from_native(">=1.3.2")
20+
assert version_range == ComposerVersionRange(
21+
constraints=(VersionConstraint(comparator=">=", version=ComposerVersion(string="1.3.2")),)
22+
)
23+
24+
25+
def test_composer_less_than():
26+
version_range = ComposerVersionRange.from_native("<1.3.2")
27+
assert version_range == ComposerVersionRange(
28+
constraints=(VersionConstraint(comparator="<", version=ComposerVersion(string="1.3.2")),)
29+
)
30+
31+
32+
def test_composer_wildcard():
33+
version_range = ComposerVersionRange.from_native("1.3.*")
34+
assert version_range == ComposerVersionRange(
35+
constraints=(
36+
VersionConstraint(comparator=">=", version=ComposerVersion(string="1.3.0")),
37+
VersionConstraint(comparator="<", version=ComposerVersion(string="1.4.0")),
38+
)
39+
)
40+
41+
42+
def test_composer_tilde_patch():
43+
version_range = ComposerVersionRange.from_native("~1.3.2")
44+
assert version_range == ComposerVersionRange(
45+
constraints=(
46+
VersionConstraint(comparator=">=", version=ComposerVersion(string="1.3.2")),
47+
VersionConstraint(comparator="<", version=ComposerVersion(string="1.4.0")),
48+
)
49+
)
50+
51+
52+
def test_composer_tilde_minor():
53+
version_range = ComposerVersionRange.from_native("~1.3")
54+
assert version_range == ComposerVersionRange(
55+
constraints=(
56+
VersionConstraint(comparator=">=", version=ComposerVersion(string="1.3.0")),
57+
VersionConstraint(comparator="<", version=ComposerVersion(string="2.0.0")),
58+
)
59+
)
60+
61+
62+
def test_composer_caret_patch():
63+
version_range = ComposerVersionRange.from_native("^1.3.2")
64+
assert version_range == ComposerVersionRange(
65+
constraints=(
66+
VersionConstraint(comparator=">=", version=ComposerVersion(string="1.3.2")),
67+
VersionConstraint(comparator="<", version=ComposerVersion(string="2.0.0")),
68+
)
69+
)
70+
71+
72+
def test_composer_caret_zero_minor():
73+
version_range = ComposerVersionRange.from_native("^0.3.2")
74+
assert version_range == ComposerVersionRange(
75+
constraints=(
76+
VersionConstraint(comparator=">=", version=ComposerVersion(string="0.3.2")),
77+
VersionConstraint(comparator="<", version=ComposerVersion(string="0.4.0")),
78+
)
79+
)
80+
81+
82+
def test_composer_range_with_multiple_constraints():
83+
version_range = ComposerVersionRange.from_native(">=1.2.3, <2.0.0")
84+
assert version_range == ComposerVersionRange(
85+
constraints=(
86+
VersionConstraint(comparator=">=", version=ComposerVersion(string="1.2.3")),
87+
VersionConstraint(comparator="<", version=ComposerVersion(string="2.0.0")),
88+
)
89+
)
90+
91+
92+
def test_composer_range_with_or_constraints():
93+
version_range = ComposerVersionRange.from_native(">=1.0.0 || <2.0.0")
94+
assert version_range == ComposerVersionRange(
95+
constraints=(
96+
VersionConstraint(comparator=">=", version=ComposerVersion(string="1.0.0")),
97+
VersionConstraint(comparator="<", version=ComposerVersion(string="2.0.0")),
98+
)
99+
)
100+
101+
102+
def test_composer_invalid_syntax():
103+
try:
104+
ComposerVersionRange.from_native(">1.0.0 <2.0.0")
105+
assert False, "Should have raised a ValueError"
106+
except ValueError:
107+
assert True
108+
109+
110+
def test_composer_range_str_representation():
111+
version_range = ComposerVersionRange.from_native(">=1.0.0, <2.0.0")
112+
assert str(version_range) == "vers:composer/>=1.0.0|<2.0.0"

tests/test_golang_version_range.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Copyright (c) nexB Inc. and others.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Visit https://aboutcode.org and https://github.com/aboutcode-org/univers for support and download.
5+
6+
from univers.version_constraint import VersionConstraint
7+
from univers.version_range import GolangVersionRange
8+
from univers.versions import GolangVersion
9+
10+
11+
def test_golang_exact_version():
12+
version_range = GolangVersionRange.from_native("v1.2.3")
13+
assert version_range == GolangVersionRange(
14+
constraints=(VersionConstraint(comparator="=", version=GolangVersion(string="v1.2.3")),)
15+
)
16+
17+
18+
def test_golang_greater_than():
19+
version_range = GolangVersionRange.from_native(">v1.2.3")
20+
assert version_range == GolangVersionRange(
21+
constraints=(VersionConstraint(comparator=">", version=GolangVersion(string="v1.2.3")),)
22+
)
23+
24+
25+
def test_golang_greater_than_or_equal():
26+
version_range = GolangVersionRange.from_native(">=v1.2.3")
27+
assert version_range == GolangVersionRange(
28+
constraints=(VersionConstraint(comparator=">=", version=GolangVersion(string="v1.2.3")),)
29+
)
30+
31+
32+
def test_golang_less_than():
33+
version_range = GolangVersionRange.from_native("<v1.2.3")
34+
assert version_range == GolangVersionRange(
35+
constraints=(VersionConstraint(comparator="<", version=GolangVersion(string="v1.2.3")),)
36+
)
37+
38+
39+
def test_golang_less_than_or_equal():
40+
version_range = GolangVersionRange.from_native("<=v1.2.3")
41+
assert version_range == GolangVersionRange(
42+
constraints=(VersionConstraint(comparator="<=", version=GolangVersion(string="v1.2.3")),)
43+
)
44+
45+
46+
def test_golang_version_range_with_multiple_constraints():
47+
version_range = GolangVersionRange.from_native(">=v1.2.3, <v2.0.0")
48+
assert version_range == GolangVersionRange(
49+
constraints=(
50+
VersionConstraint(comparator=">=", version=GolangVersion(string="v1.2.3")),
51+
VersionConstraint(comparator="<", version=GolangVersion(string="v2.0.0")),
52+
)
53+
)
54+
55+
56+
def test_golang_version_with_prerelease():
57+
version_range = GolangVersionRange.from_native("v1.2.3-beta.1")
58+
assert version_range == GolangVersionRange(
59+
constraints=(
60+
VersionConstraint(comparator="=", version=GolangVersion(string="v1.2.3-beta.1")),
61+
)
62+
)
63+
64+
65+
def test_golang_range_string_representation():
66+
version_range = GolangVersionRange.from_native(">=v1.2.3, <v2.0.0")
67+
assert str(version_range) == "vers:golang/>=1.2.3|<2.0.0"
68+
69+
70+
def test_golang_version_range_with_pre_and_build():
71+
version_range = GolangVersionRange.from_native("v1.2.3-alpha+build123")
72+
assert version_range == GolangVersionRange(
73+
constraints=(
74+
VersionConstraint(
75+
comparator="=", version=GolangVersion(string="v1.2.3-alpha+build123")
76+
),
77+
)
78+
)
79+
80+
81+
def test_golang_version_with_major_zero():
82+
version_range = GolangVersionRange.from_native("v0.1.5")
83+
assert version_range == GolangVersionRange(
84+
constraints=(VersionConstraint(comparator="=", version=GolangVersion(string="v0.1.5")),)
85+
)
86+
87+
88+
def test_golang_version_with_only_major():
89+
version_range = GolangVersionRange.from_native("v1")
90+
assert version_range == GolangVersionRange(
91+
constraints=(VersionConstraint(comparator="=", version=GolangVersion(string="v1")),)
92+
)
93+
94+
95+
def test_golang_version_with_upper_case():
96+
version_range = GolangVersionRange.from_native("V1.2.3")
97+
assert version_range == GolangVersionRange(
98+
constraints=(VersionConstraint(comparator="=", version=GolangVersion(string="v1.2.3")),)
99+
)

0 commit comments

Comments
 (0)