From 884a7e692a084ddb188a66b3bc5b5418bb523349 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Fri, 3 Mar 2023 08:41:17 +0100 Subject: [PATCH 1/9] chore: add extra config to make Unit model hashable --- filip/models/ngsi_v2/units.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/filip/models/ngsi_v2/units.py b/filip/models/ngsi_v2/units.py index 68bf6c86..4410952f 100644 --- a/filip/models/ngsi_v2/units.py +++ b/filip/models/ngsi_v2/units.py @@ -136,6 +136,8 @@ class Unit(BaseModel): class Config: extra = 'ignore' allow_population_by_field_name = True + allow_mutation = False + frozen = True @root_validator(pre=False, allow_reuse=True) def check_consistency(cls, values): From 4c2d94b056094aa79ef528ee20d25e4b50f1a3ce Mon Sep 17 00:00:00 2001 From: "thomas.storek" <20579672+tstorek@users.noreply.github.com> Date: Wed, 31 May 2023 15:16:12 +0200 Subject: [PATCH 2/9] chore: added missing test For #186 --- filip/models/ngsi_v2/units.py | 3 ++- tests/models/test_units.py | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/filip/models/ngsi_v2/units.py b/filip/models/ngsi_v2/units.py index 4410952f..e55ccf8f 100644 --- a/filip/models/ngsi_v2/units.py +++ b/filip/models/ngsi_v2/units.py @@ -205,6 +205,7 @@ def __getattr__(self, item): Return unit as attribute by name or code. Notes: Underscores will be substituted with whitespaces + Args: item: if len(row) == 0: @@ -223,7 +224,7 @@ def quantities(self): """ raise NotImplementedError("The used dataset does currently not " "contain the information about quantity") - + @lru_cache() def __getitem__(self, item: str) -> Unit: """ Get unit by name or code diff --git a/tests/models/test_units.py b/tests/models/test_units.py index ace974dc..af1cb219 100644 --- a/tests/models/test_units.py +++ b/tests/models/test_units.py @@ -2,6 +2,7 @@ Test for filip.models.units """ from unittest import TestCase +import functools from filip.models.ngsi_v2.units import \ Unit, \ Units, \ @@ -39,16 +40,47 @@ def test_unit_text(self): def test_unit_model(self): """ Test unit model + Returns: None """ + # test creation unit = Unit(**self.unit) unit_from_json = Unit.parse_raw(unit.json(by_alias=True)) self.assertEqual(unit, unit_from_json) + def test_unit_model_caching(self): + """ + Test caching of unit model + + Returns: + None + """ + + unit = Unit(**self.unit) + # testing hashing and caching + from functools import lru_cache + from time import perf_counter_ns + + self.assertEqual(unit.__hash__(), unit.__hash__()) + + @functools.lru_cache + def cache_unit(unit: Unit): + return Unit(name=unit.name) + + timers = [] + for i in range(5): + start = perf_counter_ns() + cache_unit(unit) + stop = perf_counter_ns() + timers.append(stop - start) + if i > 0: + self.assertLess(timers[i], timers[0]) + def test_units(self): """ Test units api + Returns: None """ @@ -69,6 +101,18 @@ def test_unit_validator(self): Returns: None """ + # using garbage collector to clean up all caches + import gc + gc.collect() + + # All objects collected + objects = [i for i in gc.get_objects() + if isinstance(i, functools._lru_cache_wrapper)] + + # All objects cleared + for object in objects: + object.cache_clear() + unit_data = self.unit.copy() unit_data['name'] = "celcius" with self.assertRaises(ValueError): From ae65579e0049d43b53d42e34d3333cc04deeafe2 Mon Sep 17 00:00:00 2001 From: "thomas.storek" <20579672+tstorek@users.noreply.github.com> Date: Wed, 31 May 2023 15:30:10 +0200 Subject: [PATCH 3/9] chore: added missing test For #186 --- tests/models/test_units.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/models/test_units.py b/tests/models/test_units.py index af1cb219..121e77b5 100644 --- a/tests/models/test_units.py +++ b/tests/models/test_units.py @@ -1,7 +1,7 @@ """ Test for filip.models.units """ -from unittest import TestCase +import unittest import functools from filip.models.ngsi_v2.units import \ Unit, \ @@ -11,7 +11,7 @@ load_units -class TestUnitCodes(TestCase): +class TestUnitCodes(unittest.TestCase): def setUp(self): self.units_data = load_units() @@ -101,6 +101,16 @@ def test_unit_validator(self): Returns: None """ + + unit_data = self.unit.copy() + unit_data['name'] = "celcius" + with self.assertRaises(ValueError): + Unit(**unit_data) + + def tearDown(self): + """ + clean up + """ # using garbage collector to clean up all caches import gc gc.collect() @@ -113,8 +123,7 @@ def test_unit_validator(self): for object in objects: object.cache_clear() - unit_data = self.unit.copy() - unit_data['name'] = "celcius" - with self.assertRaises(ValueError): - Unit(**unit_data) +if __name__ == '__main__': + unittest.main() + From 19089c55642bb78147c2952fac339e9ea01314a5 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Fri, 3 Mar 2023 08:41:17 +0100 Subject: [PATCH 4/9] chore: add extra config to make Unit model hashable --- filip/models/ngsi_v2/units.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/filip/models/ngsi_v2/units.py b/filip/models/ngsi_v2/units.py index cf927600..086e5128 100644 --- a/filip/models/ngsi_v2/units.py +++ b/filip/models/ngsi_v2/units.py @@ -136,6 +136,8 @@ class Unit(BaseModel): class Config: extra = 'ignore' allow_population_by_field_name = True + allow_mutation = False + frozen = True @root_validator(pre=False, allow_reuse=True) def check_consistency(cls, values): From 9a90df5ef9d70f9b3e228a957cfd42bec667f6b1 Mon Sep 17 00:00:00 2001 From: "thomas.storek" <20579672+tstorek@users.noreply.github.com> Date: Wed, 31 May 2023 15:16:12 +0200 Subject: [PATCH 5/9] chore: added missing test For #186 --- filip/models/ngsi_v2/units.py | 3 ++- tests/models/test_units.py | 43 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/filip/models/ngsi_v2/units.py b/filip/models/ngsi_v2/units.py index 086e5128..52504e52 100644 --- a/filip/models/ngsi_v2/units.py +++ b/filip/models/ngsi_v2/units.py @@ -205,6 +205,7 @@ def __getattr__(self, item): Return unit as attribute by name or code. Notes: Underscores will be substituted with whitespaces + Args: item: if len(row) == 0: @@ -223,7 +224,7 @@ def quantities(self): """ raise NotImplementedError("The used dataset does currently not " "contain the information about quantity") - + @lru_cache() def __getitem__(self, item: str) -> Unit: """ Get unit by name or code diff --git a/tests/models/test_units.py b/tests/models/test_units.py index ae07c7e0..4fe58ae5 100644 --- a/tests/models/test_units.py +++ b/tests/models/test_units.py @@ -2,6 +2,7 @@ Test for filip.models.units """ from unittest import TestCase +import functools from filip.models.ngsi_v2.units import \ Unit, \ Units, \ @@ -39,13 +40,43 @@ def test_unit_text(self): def test_unit_model(self): """ Test unit model + Returns: None """ + # test creation unit = Unit(**self.unit) unit_from_json = Unit.parse_raw(unit.json(by_alias=True)) self.assertEqual(unit, unit_from_json) + def test_unit_model_caching(self): + """ + Test caching of unit model + + Returns: + None + """ + + unit = Unit(**self.unit) + # testing hashing and caching + from functools import lru_cache + from time import perf_counter_ns + + self.assertEqual(unit.__hash__(), unit.__hash__()) + + @functools.lru_cache + def cache_unit(unit: Unit): + return Unit(name=unit.name) + + timers = [] + for i in range(5): + start = perf_counter_ns() + cache_unit(unit) + stop = perf_counter_ns() + timers.append(stop - start) + if i > 0: + self.assertLess(timers[i], timers[0]) + def test_units(self): """ Test units api @@ -78,6 +109,18 @@ def test_unit_validator(self): Returns: None """ + # using garbage collector to clean up all caches + import gc + gc.collect() + + # All objects collected + objects = [i for i in gc.get_objects() + if isinstance(i, functools._lru_cache_wrapper)] + + # All objects cleared + for object in objects: + object.cache_clear() + unit_data = self.unit.copy() unit_data['name'] = "celcius" with self.assertRaises(ValueError): From c39fb21d2191ac1166cfd58590a1bc3d8512fffb Mon Sep 17 00:00:00 2001 From: "thomas.storek" <20579672+tstorek@users.noreply.github.com> Date: Wed, 31 May 2023 15:30:10 +0200 Subject: [PATCH 6/9] chore: added missing test For #186 --- tests/models/test_units.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/models/test_units.py b/tests/models/test_units.py index 4fe58ae5..d2baea8d 100644 --- a/tests/models/test_units.py +++ b/tests/models/test_units.py @@ -1,7 +1,7 @@ """ Test for filip.models.units """ -from unittest import TestCase +import unittest import functools from filip.models.ngsi_v2.units import \ Unit, \ @@ -11,7 +11,7 @@ load_units -class TestUnitCodes(TestCase): +class TestUnitCodes(unittest.TestCase): def setUp(self): self.units_data = load_units() @@ -109,6 +109,16 @@ def test_unit_validator(self): Returns: None """ + + unit_data = self.unit.copy() + unit_data['name'] = "celcius" + with self.assertRaises(ValueError): + Unit(**unit_data) + + def tearDown(self): + """ + clean up + """ # using garbage collector to clean up all caches import gc gc.collect() @@ -121,8 +131,7 @@ def test_unit_validator(self): for object in objects: object.cache_clear() - unit_data = self.unit.copy() - unit_data['name'] = "celcius" - with self.assertRaises(ValueError): - Unit(**unit_data) +if __name__ == '__main__': + unittest.main() + From 99fa8706460417d1aaa30b9923263cbe6d07aeb2 Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Fri, 2 Jun 2023 11:56:00 +0200 Subject: [PATCH 7/9] fix: compatible with python37 --- tests/models/test_units.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/models/test_units.py b/tests/models/test_units.py index d2baea8d..962432a4 100644 --- a/tests/models/test_units.py +++ b/tests/models/test_units.py @@ -64,7 +64,7 @@ def test_unit_model_caching(self): self.assertEqual(unit.__hash__(), unit.__hash__()) - @functools.lru_cache + @functools.lru_cache(maxsize=128) def cache_unit(unit: Unit): return Unit(name=unit.name) From 9aa4beca2167c95d3976eedf841401807608ddff Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Fri, 2 Jun 2023 14:51:34 +0200 Subject: [PATCH 8/9] chore: add previous change in CHANGELOG.md --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a8c0be2..943fdbf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ -#### v0.2.5 -- fixed inconsistency of `entity_type` as required argument ([#188](https://github.com/RWTH-EBC/FiLiP/issues/188)) +#### v0.2.6 +- fixed make unit model hashable for caching ([#186](https://github.com/RWTH-EBC/FiLiP/issues/186)) +- fixed false string matching `__getitem__` of Units ([#189](https://github.com/RWTH-EBC/FiLiP/issues/189)) +- chore add message to all KeyError ([#191](https://github.com/RWTH-EBC/FiLiP/issues/191)) #### v0.2.5 - fixed service group edition not working ([#170](https://github.com/RWTH-EBC/FiLiP/issues/170)) From 8ba63643cf12b3dbbd99e6e7caa532600ae2a46f Mon Sep 17 00:00:00 2001 From: JunsongDu Date: Fri, 2 Jun 2023 14:52:08 +0200 Subject: [PATCH 9/9] chore: adapt code pattern --- filip/semantics/semantics_models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filip/semantics/semantics_models.py b/filip/semantics/semantics_models.py index 1a09c7de..7caa9cc8 100644 --- a/filip/semantics/semantics_models.py +++ b/filip/semantics/semantics_models.py @@ -1132,7 +1132,8 @@ def remove(self, v): elif isinstance(v, SemanticIndividual): self._set.remove(v.get_name()) else: - raise KeyError(f"v is neither of type SemanticIndividual nor SemanticClass but {type(v)}") + raise KeyError(f"v is neither of type SemanticIndividual nor " + f"SemanticClass but {type(v)}") def _add_inverse(self, v: 'SemanticClass'): """