From 6d47bdff6ee40f0a9e9af3547d92f0f7bb839202 Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Mon, 10 Jan 2022 17:25:41 +0100 Subject: [PATCH 1/2] Ensure that VersionRange is immutable and hashable See #20 We now ensure that a `version_constraints` attribute is a tuple Signed-off-by: Philippe Ombredanne --- src/univers/version_range.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/univers/version_range.py b/src/univers/version_range.py index d7bc141f..1455ac76 100644 --- a/src/univers/version_range.py +++ b/src/univers/version_range.py @@ -40,7 +40,7 @@ class VersionRange: # A list of lists of VersionConstraint that are signposts on the versions # timeline - constraints = attr.ib(type=list, default=attr.Factory(list)) + constraints = attr.ib(type=tuple, converter=tuple, default=attr.Factory(tuple)) def __attrs_post_init__(self, *args, **kwargs): self.constraints.sort() From 13ee4c8fa827b88b40e4788bc37884752c786bc8 Mon Sep 17 00:00:00 2001 From: Hritik Vijay Date: Mon, 10 Jan 2022 22:33:02 +0530 Subject: [PATCH 2/2] Resolve CI fails converters are executed before __attrs_post_init__ thus we need to convert explicitly in __attrs_post_init__ See: https://www.attrs.org/en/stable/init.html#order-of-execution Signed-off-by: Hritik Vijay --- src/univers/version_range.py | 7 +++++-- tests/test_version_range.py | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/univers/version_range.py b/src/univers/version_range.py index 1455ac76..1f2f5285 100644 --- a/src/univers/version_range.py +++ b/src/univers/version_range.py @@ -40,10 +40,13 @@ class VersionRange: # A list of lists of VersionConstraint that are signposts on the versions # timeline - constraints = attr.ib(type=tuple, converter=tuple, default=attr.Factory(tuple)) + constraints = attr.ib(type=tuple, default=attr.Factory(tuple)) def __attrs_post_init__(self, *args, **kwargs): - self.constraints.sort() + constraints = tuple(sorted(self.constraints)) + # Notes: setattr is used because this is an immutable frozen instance. + # See https://www.attrs.org/en/stable/init.html?#post-init + object.__setattr__(self, "constraints", constraints) @classmethod def from_native(cls, string): diff --git a/tests/test_version_range.py b/tests/test_version_range.py index e5ad4328..06d601d7 100644 --- a/tests/test_version_range.py +++ b/tests/test_version_range.py @@ -41,7 +41,7 @@ def test_VersionRange_from_string_pypi(self): version_range = VersionRange.from_string(vers) assert version_range.scheme == "pypi" # note the sorting taking place - expected = [ + expected = ( VersionConstraint(comparator="=", version=PypiVersion(string="0.0.0")), VersionConstraint(comparator="=", version=PypiVersion(string="0.0.1")), VersionConstraint(comparator="=", version=PypiVersion(string="0.0.2")), @@ -49,7 +49,7 @@ def test_VersionRange_from_string_pypi(self): VersionConstraint(comparator="=", version=PypiVersion(string="0.0.4")), VersionConstraint(comparator="=", version=PypiVersion(string="0.0.5")), VersionConstraint(comparator="=", version=PypiVersion(string="0.0.6")), - ] + ) assert version_range.constraints == expected # note the sorting taking place assert str(version_range) == "vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|0.0.4|0.0.5|0.0.6" @@ -146,7 +146,7 @@ def test_GemVersionRange_from_native_range_with_pessimistic_operator(self): gem_range = "~>2.0.8" version_range = GemVersionRange.from_native(gem_range) assert version_range.to_string() == "vers:gem/>=2.0.8|<2.1" - assert version_range.constraints == [ + assert version_range.constraints == ( VersionConstraint(comparator=">=", version=RubygemsVersion(string="2.0.8")), VersionConstraint(comparator="<", version=RubygemsVersion(string="2.1")), - ] + )