diff --git a/src/univers/semver.py b/src/univers/semver.py index e7abb3b3..5b7469db 100644 --- a/src/univers/semver.py +++ b/src/univers/semver.py @@ -4,10 +4,9 @@ # # Visit https://aboutcode.org and https://github.com/nexB/univers for support and download. -import semantic_version - from univers.utils import remove_spaces from univers.version_constraint import VersionConstraint +from univers.versions import SemverVersion """ node-semver and Rubygems semver-like related utilities. @@ -16,14 +15,15 @@ def get_caret_constraints(string): """ - Return a tuple of two VersionConstraint representing the lower and upper - bound of version constraint ``string`` that contains a caret node-semver- - like range. Raise a ValueError if this is not a caret range. + Return a tuple of two VersionConstraint of ``SemverVersion`` representing + the lower and upper bound of version constraint ``string`` that contains a + caret node-semver- like range. Raise a ValueError if this is not a caret + range. For example: >>> lower_bound, upper_bound = get_caret_constraints("^1.0.2") - >>> vlow = semantic_version.Version("1.0.2") - >>> vup = semantic_version.Version("2.0.0") + >>> vlow = SemverVersion("1.0.2") + >>> vup = SemverVersion("2.0.0") >>> assert lower_bound == VersionConstraint(comparator=">=", version=vlow) >>> assert upper_bound == VersionConstraint(comparator="<", version=vup) """ @@ -32,8 +32,8 @@ def get_caret_constraints(string): raise ValueError(f"Invalid caret version range: {string!r}") version = string.lstrip("^") - lower_bound = semantic_version.Version(version) - upper_bound = lower_bound.next_major() + lower_bound = SemverVersion(version) + upper_bound = SemverVersion(str(lower_bound.value.next_major())) return ( VersionConstraint(comparator=">=", version=lower_bound), @@ -43,14 +43,15 @@ def get_caret_constraints(string): def get_tilde_constraints(string, operator="~"): """ - Return a tuple of two VersionConstraint representing the lower and upper - bound of a version range ``string`` that contains a tilde node-semver-like - range. Raise a ValueError if this is not a tilde range. + Return a tuple of two VersionConstraint of ``SemverVersion`` representing + the lower and upper bound of a version range ``string`` that contains a + tilde node-semver-like range. + Raise a ValueError if this is not a tilde range. For example: >>> lower_bound, upper_bound = get_tilde_constraints("~1.0.2") - >>> vlow = semantic_version.Version("1.0.2") - >>> vup = semantic_version.Version("1.1.0") + >>> vlow = SemverVersion("1.0.2") + >>> vup = SemverVersion("1.1.0") >>> assert lower_bound == VersionConstraint(comparator=">=", version=vlow) >>> assert upper_bound == VersionConstraint(comparator="<", version=vup) """ @@ -59,8 +60,8 @@ def get_tilde_constraints(string, operator="~"): raise ValueError(f"Invalid version range: {string!r} " f"does not start with {operator!r}") version = string.lstrip(operator) - lower_bound = semantic_version.Version(version) - upper_bound = lower_bound.next_minor() + lower_bound = SemverVersion(version) + upper_bound = SemverVersion(str(lower_bound.value.next_minor())) return ( VersionConstraint(comparator=">=", version=lower_bound), @@ -71,14 +72,15 @@ def get_tilde_constraints(string, operator="~"): # FIXME: this is unlikely correct https://github.com/npm/node-semver/issues/112 def get_pessimistic_constraints(string): """ - Return a tuple of two VersionConstraint representing the lower and upper - bound of version range ``string`` that contains a pessimistic Ruby range. - Raise a ValueError if this is not a pessimistic Rubygems range. + Return a tuple of two VersionConstraint of ``SemverVersion`` representing + the lower and upper bound of version range ``string`` that contains a + pessimistic Ruby range. Raise a ValueError if this is not a pessimistic + Rubygems range. For example: >>> lower_bound, upper_bound = get_pessimistic_constraints("~>2.0.8") - >>> vlow = semantic_version.Version("2.0.8") - >>> vup = semantic_version.Version("2.1.0") + >>> vlow = SemverVersion("2.0.8") + >>> vup = SemverVersion("2.1.0") >>> assert lower_bound == VersionConstraint(comparator=">=", version=vlow) >>> assert upper_bound == VersionConstraint(comparator="<", version=vup) """ diff --git a/src/univers/version_constraint.py b/src/univers/version_constraint.py index 8ae3a3d9..105cd49d 100644 --- a/src/univers/version_constraint.py +++ b/src/univers/version_constraint.py @@ -66,12 +66,15 @@ class VersionConstraint: # one of the COMPARATORS comparator = attr.ib(type=str, default="=") - # a Version subclass instance or None + # a Version subclass instance version = attr.ib(type=Version, default=None) # a function for the comparator comp_operator = attr.ib(default=None, repr=False) + # a Version subclass + version_class = attr.ib(type=Version, default=None, repr=False) + def __attrs_post_init__(self): # Notes: setattr is used because this is an immutable frozen instance. # See https://www.attrs.org/en/stable/init.html?#post-init @@ -80,15 +83,26 @@ def __attrs_post_init__(self): except KeyError as e: raise ValueError(f"Unknown comparator: {self.comparator}") from e + if self.version and not isinstance(self.version, Version): + raise TypeError( + f"version must be a 'Version' instance and not: {self.version.__class__!r}" + ) + + if not self.version_class: + if self.version: + object.__setattr__(self, "version_class", self.version.__class__) + else: + raise ValueError("Cannot build a VersionConstraint without a version class") + def __str__(self): """ Return a string representing this constraint. For example:: - >>> assert str(VersionConstraint(comparator=">=", version="2.3")) == ">=2.3" - >>> assert str(VersionConstraint(comparator="*")) == "*" - >>> assert str(VersionConstraint(comparator="<", version="2.3")) == "<2.3" - >>> assert str(VersionConstraint(comparator="=", version="2.3.0")) == "2.3.0" - >>> assert str(VersionConstraint(version="2.3.0")) == "2.3.0" + >>> assert str(VersionConstraint(comparator=">=", version=Version("2.3"))) == ">=2.3" + >>> assert str(VersionConstraint(comparator="*", version_class=Version)) == "*" + >>> assert str(VersionConstraint(comparator="<", version=Version("2.3"))) == "<2.3" + >>> assert str(VersionConstraint(comparator="=", version=Version("2.3.0"))) == "2.3.0" + >>> assert str(VersionConstraint(version=Version("2.3.0"))) == "2.3.0" """ if self.comparator == "*": return "*" @@ -143,7 +157,7 @@ def from_string(cls, string, version_class): version = None else: version = version_class(version) - return cls(comparator, version) + return cls(comparator=comparator, version=version, version_class=version_class) @staticmethod def split(string): @@ -210,11 +224,10 @@ def __contains__(self, version): >>> assert v24 in VersionConstraint(comparator="<=", version=v24) >>> assert v24 not in VersionConstraint(comparator="<", version=v24) """ - - if not isinstance(version, self.version.__class__): + if not isinstance(version, self.version_class): raise ValueError( f"Cannot compare {version.__class__!r} instance " - f"with {self.version.__class__!r} instance." + f"with {self.version_class!r} instance." ) return self.comp_operator(version, self.version) diff --git a/src/univers/version_range.py b/src/univers/version_range.py index 22462e62..080ac9f7 100644 --- a/src/univers/version_range.py +++ b/src/univers/version_range.py @@ -114,7 +114,9 @@ def from_string(cls, vers, simplify=False, validate=False): if constraints.startswith("*"): if constraints != "*": raise ValueError(f"{vers!r} contains an invalid '*' constraint.") - return range_class([VersionConstraint.from_string(string="*", version_class=None)]) + return range_class( + [VersionConstraint.from_string(string="*", version_class=version_class)] + ) parsed_constraints = [] @@ -774,7 +776,9 @@ def from_native(cls, string): """ cleaned = remove_spaces(string).lower() if cleaned == "all": - return cls(constraints=[VersionConstraint(comparator="*")]) + return cls( + constraints=[VersionConstraint(comparator="*", version_class=cls.version_class)] + ) constraints = [] diff --git a/tests/test_version_range.py b/tests/test_version_range.py index 06d601d7..7f83e75f 100644 --- a/tests/test_version_range.py +++ b/tests/test_version_range.py @@ -150,3 +150,8 @@ def test_GemVersionRange_from_native_range_with_pessimistic_operator(self): VersionConstraint(comparator=">=", version=RubygemsVersion(string="2.0.8")), VersionConstraint(comparator="<", version=RubygemsVersion(string="2.1")), ) + + def test_VersionRange_contains_works_for_star_range(self): + from univers.versions import SemverVersion + + SemverVersion("1.0.0") in VersionRange.from_string("vers:nginx/*")