From b1a108d59a22cfcd5ad61863dbe1dc2bb34677c9 Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Thu, 10 Nov 2022 09:17:13 +0000 Subject: [PATCH 1/6] add tests to test hash and __eq__ --- src/icalendar/tests/test_unit_prop.py | 57 ++++++++++++++++----------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/icalendar/tests/test_unit_prop.py b/src/icalendar/tests/test_unit_prop.py index 05493205..32023900 100644 --- a/src/icalendar/tests/test_unit_prop.py +++ b/src/icalendar/tests/test_unit_prop.py @@ -6,8 +6,11 @@ import unittest from icalendar.prop import vDatetime from icalendar.windows_to_olson import WINDOWS_TO_OLSON - +import pytest import pytz +from ..prop import vDDDTypes +from copy import deepcopy + class TestProp(unittest.TestCase): @@ -112,27 +115,6 @@ def test_prop_vDDDTypes(self): # Bad input self.assertRaises(ValueError, vDDDTypes, 42) - def test_vDDDTypes_equivalance(self): - from ..prop import vDDDTypes - from copy import deepcopy - - vDDDTypes = [ - vDDDTypes(pytz.timezone('US/Eastern').localize(datetime(year=2022, month=7, day=22, hour=12, minute=7))), - vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7)), - vDDDTypes(date(year=2022, month=7, day=22)), - vDDDTypes(time(hour=22, minute=7, second=2))] - - for v_type in vDDDTypes: - self.assertEqual(v_type, deepcopy(v_type)) - for other in vDDDTypes: - if other is v_type: - continue - self.assertNotEqual(v_type, other) - - # see if equivalnce fails for other types - self.assertNotEqual(v_type, 42) - self.assertNotEqual(v_type, 'test') - def test_prop_vDate(self): from ..prop import vDate @@ -518,6 +500,37 @@ def test_prop_TypesFactory(self): ) + +vDDDTypes = [ + vDDDTypes(pytz.timezone('US/Eastern').localize(datetime(year=2022, month=7, day=22, hour=12, minute=7))), + vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7)), + vDDDTypes(date(year=2022, month=7, day=22)), + vDDDTypes(time(hour=22, minute=7, second=2)) +] + +def identity(x): + return x + +@pytest.mark.parametrize("map", [ + deepcopy, + identity, + hash, +]) +@pytest.mark.parametrize("v_type", vDDDTypes) +@pytest.mark.parametrize("other", vDDDTypes) +def test_vDDDTypes_equivalance(map, v_type, other): + if v_type is other: + assert map(v_type) == map(other), f"identity implies equality: {map.__name__}()" + assert not (map(v_type) != map(other)), f"identity implies equality: {map.__name__}()" + else: + assert map(v_type) != map(other), f"expected inequality: {map.__name__}()" + assert not (map(v_type) == map(other)), f"expected inequality: {map.__name__}()" + +@pytest.mark.parametrize("v_type", vDDDTypes) +def test_inequality_with_different_types(v_type): + assert v_type != 42 + assert v_type != 'test' + class TestPropertyValues(unittest.TestCase): def test_vDDDLists_timezone(self): From 185065ebbd5dc5d5cad3e27777c4a9060bee0e50 Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Thu, 10 Nov 2022 09:23:51 +0000 Subject: [PATCH 2/6] implement hash based on the dt value --- src/icalendar/prop.py | 3 +++ src/icalendar/tests/test_unit_prop.py | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/icalendar/prop.py b/src/icalendar/prop.py index f6c261c0..966a29ab 100644 --- a/src/icalendar/prop.py +++ b/src/icalendar/prop.py @@ -323,6 +323,9 @@ def __eq__(self, other): return self.params == other.params return False + def __hash__(self): + return hash(self.dt) + @classmethod def from_ical(cls, ical, timezone=None): if isinstance(ical, cls): diff --git a/src/icalendar/tests/test_unit_prop.py b/src/icalendar/tests/test_unit_prop.py index 32023900..ba977dd9 100644 --- a/src/icalendar/tests/test_unit_prop.py +++ b/src/icalendar/tests/test_unit_prop.py @@ -501,7 +501,7 @@ def test_prop_TypesFactory(self): -vDDDTypes = [ +vDDDTypes_list = [ vDDDTypes(pytz.timezone('US/Eastern').localize(datetime(year=2022, month=7, day=22, hour=12, minute=7))), vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7)), vDDDTypes(date(year=2022, month=7, day=22)), @@ -516,8 +516,8 @@ def identity(x): identity, hash, ]) -@pytest.mark.parametrize("v_type", vDDDTypes) -@pytest.mark.parametrize("other", vDDDTypes) +@pytest.mark.parametrize("v_type", vDDDTypes_list) +@pytest.mark.parametrize("other", vDDDTypes_list) def test_vDDDTypes_equivalance(map, v_type, other): if v_type is other: assert map(v_type) == map(other), f"identity implies equality: {map.__name__}()" @@ -526,7 +526,7 @@ def test_vDDDTypes_equivalance(map, v_type, other): assert map(v_type) != map(other), f"expected inequality: {map.__name__}()" assert not (map(v_type) == map(other)), f"expected inequality: {map.__name__}()" -@pytest.mark.parametrize("v_type", vDDDTypes) +@pytest.mark.parametrize("v_type", vDDDTypes_list) def test_inequality_with_different_types(v_type): assert v_type != 42 assert v_type != 'test' From 53567ef79e5efdfdf82710aa33b147dd8ba7ad7e Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Thu, 10 Nov 2022 09:25:43 +0000 Subject: [PATCH 3/6] make tests fail if the parameters equal This __eq__ implementation is faulty. The .dt property should be compared, too --- src/icalendar/tests/test_unit_prop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/icalendar/tests/test_unit_prop.py b/src/icalendar/tests/test_unit_prop.py index ba977dd9..e2a9daf2 100644 --- a/src/icalendar/tests/test_unit_prop.py +++ b/src/icalendar/tests/test_unit_prop.py @@ -505,6 +505,7 @@ def test_prop_TypesFactory(self): vDDDTypes(pytz.timezone('US/Eastern').localize(datetime(year=2022, month=7, day=22, hour=12, minute=7))), vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7)), vDDDTypes(date(year=2022, month=7, day=22)), + vDDDTypes(date(year=2022, month=7, day=23)), vDDDTypes(time(hour=22, minute=7, second=2)) ] From 6808972f325cf55b656abf8910ae07313b55c157 Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Thu, 10 Nov 2022 09:27:16 +0000 Subject: [PATCH 4/6] add vDDDTypes.dt to __eq__ --- src/icalendar/prop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/icalendar/prop.py b/src/icalendar/prop.py index 966a29ab..dcbb9260 100644 --- a/src/icalendar/prop.py +++ b/src/icalendar/prop.py @@ -320,7 +320,7 @@ def to_ical(self): def __eq__(self, other): if isinstance(other, vDDDTypes): - return self.params == other.params + return self.params == other.params and self.dt == other.dt return False def __hash__(self): From b486ec9542caaa1707124a06c01cc6606b658555 Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Thu, 10 Nov 2022 09:45:07 +0000 Subject: [PATCH 5/6] add changelog entry --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b46dae7f..d0682ba3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,11 +14,11 @@ Breaking changes: New features: -- ... +- vDDDTypes is hashable #487 #492 [niccokunzmann] Bug fixes: -- ... +- vDDDTypes' equality also checks the dt attribute #497 #492 [niccokunzmann] 5.0.2 (2022-11-03) ------------------ From ae9ac71561320dc30d06c5d7cc170f662550adb0 Mon Sep 17 00:00:00 2001 From: Nicco Kunzmann Date: Wed, 16 Nov 2022 22:40:30 +0000 Subject: [PATCH 6/6] add a check that a timezone difference matters to equality see https://github.com/collective/icalendar/pull/492#discussion_r1020730916 --- src/icalendar/tests/test_unit_prop.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/icalendar/tests/test_unit_prop.py b/src/icalendar/tests/test_unit_prop.py index e2a9daf2..b732b233 100644 --- a/src/icalendar/tests/test_unit_prop.py +++ b/src/icalendar/tests/test_unit_prop.py @@ -4,13 +4,12 @@ from datetime import timedelta from icalendar.parser import Parameters import unittest -from icalendar.prop import vDatetime +from icalendar.prop import vDatetime, vDDDTypes from icalendar.windows_to_olson import WINDOWS_TO_OLSON import pytest import pytz -from ..prop import vDDDTypes from copy import deepcopy - +from dateutil import tz class TestProp(unittest.TestCase): @@ -504,6 +503,7 @@ def test_prop_TypesFactory(self): vDDDTypes_list = [ vDDDTypes(pytz.timezone('US/Eastern').localize(datetime(year=2022, month=7, day=22, hour=12, minute=7))), vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7)), + vDDDTypes(datetime(year=2022, month=7, day=22, hour=12, minute=7, tzinfo=tz.UTC)), vDDDTypes(date(year=2022, month=7, day=22)), vDDDTypes(date(year=2022, month=7, day=23)), vDDDTypes(time(hour=22, minute=7, second=2))