diff --git a/CHANGELOG.md b/CHANGELOG.md index a32e16deba8103a3e037c7bedfeb8fc0e4c55a6b..196d4b7189c39ae361eeb1bb7f7782be150c7d88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 as `cuid` and `flags`, respectively. ### Changed ### +* Renamed the `filter` function of Container, ParentList and PropertyList to `filter_by_identity`. ### Deprecated ### diff --git a/src/doc/tutorials/complex_data_models.rst b/src/doc/tutorials/complex_data_models.rst index 6c9520c94105e4106343ed2ab1a4c807d669d977..52757c320b42f18b4b24ab9b7575e7bd0becc252 100644 --- a/src/doc/tutorials/complex_data_models.rst +++ b/src/doc/tutorials/complex_data_models.rst @@ -100,29 +100,29 @@ entities. A short example: properties = r.properties # As r only has one property with id 101, this returns a list containing only p1_1 - properties.filter(pid=101) + properties.filter_by_identity(pid=101) # Result: [p1_1] # Filtering with name="Property 1" returns both p1_1 and p1_2, as they share their name - properties.filter(name="Property 1") + properties.filter_by_identity(name="Property 1") # Result: [p1_1, p1_2] # If both name and pid are given, matching is based only on pid for all entities that have an id - properties.filter(pid="102", name="Other Property") + properties.filter_by_identity(pid="102", name="Other Property") # Result: [p2_1, p2_2, p2_3] - # However, filtering with name="Property 1" and id=101 returns both p1_1 and p1_2, because + # However, filter_by_identity with name="Property 1" and id=101 returns both p1_1 and p1_2, because # p1_2 does not have an id and matches the name - properties.filter(pid="101", name="Property 1") + properties.filter_by_identity(pid="101", name="Property 1") # Result: [p1_1, p1_2] # We can also filter using an entity, in which case the name and id of the entity are used: - properties.filter(pid="102", name="Property 2") == properties.filter(p2_1) + properties.filter_by_identity(pid="102", name="Property 2") == properties.filter_by_identity(p2_1) # Result: True # If we only need properties that match both id and name, we can set the parameter # conjunction to True: - properties.filter(pid="102", name="Property 2", conjunction=True) + properties.filter_by_identity(pid="102", name="Property 2", conjunction=True) # Result: [p2_1] The filter function of ParentList works analogously. @@ -140,5 +140,5 @@ A short example: p1 = db.Property(id=101, name="Property 1") p2 = db.Property(name="Property 2") c = db.Container().extend([p1,p2]) - c.filter(name="Property 1") + c.filter_by_identity(name="Property 1") # Result: [p1] diff --git a/src/linkahead/apiutils.py b/src/linkahead/apiutils.py index 1aa127d31d6456076ffdd6dc5b591c3bb2f7b0b7..b2a612faea1616c64b7e78575156abccfdb29e61 100644 --- a/src/linkahead/apiutils.py +++ b/src/linkahead/apiutils.py @@ -386,7 +386,7 @@ def compare_entities(entity0: Optional[Entity] = None, for prop in entity0.properties: # ToDo: Would making id default break anything? key = prop.name if prop.name is not None else prop.id - matching = entity1.properties.filter(prop) + matching = entity1.properties.filter_by_identity(prop) if len(matching) == 0: # entity1 has prop, entity0 does not diff[0]["properties"][key] = {} @@ -440,7 +440,7 @@ def compare_entities(entity0: Optional[Entity] = None, for prop in entity1.properties: key = prop.name if prop.name is not None else prop.id # check how often the property appears in entity0 - num_prop_in_ent0 = len(entity0.properties.filter(prop)) + num_prop_in_ent0 = len(entity0.properties.filter_by_identity(prop)) if num_prop_in_ent0 == 0: # property is only present in entity0 - add to diff diff[1]["properties"][key] = {} @@ -455,7 +455,7 @@ def compare_entities(entity0: Optional[Entity] = None, (1, entity1.parents, entity0)]: for parent in parents: key = parent.name if parent.name is not None else parent.id - matching = other_entity.parents.filter(parent) + matching = other_entity.parents.filter_by_identity(parent) if len(matching) == 0: diff[index]["parents"].append(key) continue diff --git a/src/linkahead/common/models.py b/src/linkahead/common/models.py index 8bd8eacda1035761bbc7b9aff4134fc45f682da0..7bd08a6638e931176497e08509b440133625e6c4 100644 --- a/src/linkahead/common/models.py +++ b/src/linkahead/common/models.py @@ -2572,7 +2572,11 @@ class PropertyList(list): return xml2str(xml) - def filter(self, prop: Optional[Property] = None, + def filter(self, *args, **kwargs): + warnings.warn(DeprecationWarning("This function was renamed to filter_by_identity.")) + return self.filter_by_identity(*args, **kwargs) + + def filter_by_identity(self, prop: Optional[Property] = None, pid: Union[None, str, int] = None, name: Optional[str] = None, conjunction: bool = False) -> list: @@ -2580,7 +2584,7 @@ class PropertyList(list): Return all Properties from the given PropertyList that match the selection criteria. - Please refer to the documentation of _filter_entity_list for a detailed + Please refer to the documentation of _filter_entity_list_by_identity for a detailed description of behaviour. Params @@ -2601,7 +2605,7 @@ class PropertyList(list): matches : list List containing all matching Properties """ - return _filter_entity_list(self, pid=pid, name=name, entity=prop, + return _filter_entity_list_by_identity(self, pid=pid, name=name, entity=prop, conjunction=conjunction) def _get_entity_by_cuid(self, cuid: str): @@ -2739,7 +2743,11 @@ class ParentList(list): return xml2str(xml) - def filter(self, parent: Optional[Parent] = None, + def filter(self, *args, **kwargs): + warnings.warn(DeprecationWarning("This function was renamed to filter_by_identity.")) + return self.filter_by_identity(*args, **kwargs) + + def filter_by_identity(self, parent: Optional[Parent] = None, pid: Union[None, str, int] = None, name: Optional[str] = None, conjunction: bool = False) -> list: @@ -2747,7 +2755,7 @@ class ParentList(list): Return all Parents from the given ParentList that match the selection criteria. - Please refer to the documentation of _filter_entity_list for a detailed + Please refer to the documentation of _filter_entity_list_by_identity for a detailed description of behaviour. Params @@ -2770,7 +2778,7 @@ class ParentList(list): matches : list List containing all matching Parents """ - return _filter_entity_list(self, pid=pid, name=name, entity=parent, + return _filter_entity_list_by_identity(self, pid=pid, name=name, entity=parent, conjunction=conjunction) def remove(self, parent: Union[Entity, int, str]): @@ -3738,21 +3746,21 @@ class Container(list): return sync_dict - def filter(self, entity: Optional[Entity] = None, - pid: Union[None, str, int] = None, + def filter_by_identity(self, entity: Optional[Entity] = None, + entity_id: Union[None, str, int] = None, name: Optional[str] = None, conjunction: bool = False) -> list: """ Return all Entities from this Container that match the selection criteria. - Please refer to the documentation of _filter_entity_list for a detailed + Please refer to the documentation of _filter_entity_list_by_identity for a detailed description of behaviour. Params ------ entity : Entity Entity to match name and ID with - pid : str, int + entity_id : str, int Parent ID to match name : str Parent name to match @@ -3766,7 +3774,7 @@ class Container(list): matches : list List containing all matching Entities """ - return _filter_entity_list(self, pid=pid, name=name, entity=entity, + return _filter_entity_list_by_identity(self, pid=entity_id, name=name, entity=entity, conjunction=conjunction) @staticmethod @@ -5576,7 +5584,7 @@ def delete(ids: Union[list[int], range], raise_exception_on_error: bool = True): return c.delete(raise_exception_on_error=raise_exception_on_error) -def _filter_entity_list(listobject: list[Entity], +def _filter_entity_list_by_identity(listobject: list[Entity], entity: Optional[Entity] = None, pid: Union[None, str, int] = None, name: Optional[str] = None, diff --git a/unittests/test_container.py b/unittests/test_container.py index 715a6eb8f409c489413120ca72857808029d323a..54e876747a0581dba0293ce5a94ee40ac74abfb7 100644 --- a/unittests/test_container.py +++ b/unittests/test_container.py @@ -203,10 +203,12 @@ def test_container_slicing(): def test_container_filter(): - # this is a very rudimentary test since filter is based on _filter_entity_list which is tested + # this is a very rudimentary test since filter_by_identity is based on + # _filter_entity_list_by_identity which is tested # separately cont = db.Container() cont.extend([db.Record(name=f"TestRec{ii+1}") for ii in range(5)]) - recs = cont.filter(name="TestRec2") - assert len(recs) == 1 + + recs = cont.filter_by_identity(name="TestRec2") + assert len(recs)== 1 recs[0].name == "TestRec2" diff --git a/unittests/test_entity.py b/unittests/test_entity.py index 2127ce028f4de55b8ef0ca704c1e69959c24ba82..da057783df50a81a38074e84ddb9822c8e86cc61 100644 --- a/unittests/test_entity.py +++ b/unittests/test_entity.py @@ -29,6 +29,7 @@ import unittest import linkahead from linkahead import (INTEGER, Entity, Parent, Property, Record, RecordType, configure_connection) +import warnings from linkahead.common.models import SPECIAL_ATTRIBUTES from linkahead.connection.mockup import MockUpServerConnection from lxml import etree @@ -161,7 +162,7 @@ def test_property_list(): pl.append(p3) -def test_filter(): +def test_filter_by_identity(): rt1 = RecordType(id=100) rt2 = RecordType(id=101, name="RT") rt3 = RecordType(name="") @@ -184,7 +185,7 @@ def test_filter(): for coll in [entity.properties, entity.parents]: for ent in test_ents: assert ent not in coll - assert ent not in coll.filter(ent) + assert ent not in coll.filter_by_identity(ent) # Checks with each type t, t_props, t_pars = entity, entity.properties, entity.parents @@ -194,23 +195,23 @@ def test_filter(): tp1 = t.properties[-1] t.add_property(p3) tp3 = t.properties[-1] - assert len(t_props.filter(pid=100)) == 1 - assert tp1 in t_props.filter(pid=100) - assert len(t_props.filter(pid="100")) == 1 - assert tp1 in t_props.filter(pid="100") - assert len(t_props.filter(pid=101, name="RT")) == 1 - assert tp3 in t_props.filter(pid=101, name="RT") + assert len(t_props.filter_by_identity(pid=100)) == 1 + assert tp1 in t_props.filter_by_identity(pid=100) + assert len(t_props.filter_by_identity(pid="100")) == 1 + assert tp1 in t_props.filter_by_identity(pid="100") + assert len(t_props.filter_by_identity(pid=101, name="RT")) == 1 + assert tp3 in t_props.filter_by_identity(pid=101, name="RT") for entity in [rt1, p2, r1, r2]: - assert entity not in t_props.filter(pid=100) - assert tp1 in t_props.filter(entity) + assert entity not in t_props.filter_by_identity(pid=100) + assert tp1 in t_props.filter_by_identity(entity) # Check that direct addition (not wrapped) works t_props.append(p2) tp2 = t_props[-1] - assert tp2 in t_props.filter(pid=100) - assert tp2 not in t_props.filter(pid=101, name="RT") + assert tp2 in t_props.filter_by_identity(pid=100) + assert tp2 not in t_props.filter_by_identity(pid=101, name="RT") for entity in [rt1, r1, r2]: - assert entity not in t_props.filter(pid=100) - assert tp2 in t_props.filter(entity) + assert entity not in t_props.filter_by_identity(pid=100) + assert tp2 in t_props.filter_by_identity(entity) # Parents # Filtering with both name and id @@ -218,67 +219,80 @@ def test_filter(): tr3 = t.parents[-1] t.add_parent(r5) tr5 = t.parents[-1] - assert tr3 in t_pars.filter(pid=101) - assert tr5 not in t_pars.filter(pid=101) - assert tr3 not in t_pars.filter(name="R") - assert tr5 in t_pars.filter(name="R") - assert tr3 in t_pars.filter(pid=101, name="R") - assert tr5 not in t_pars.filter(pid=101, name="R") - assert tr3 not in t_pars.filter(pid=104, name="RT") - assert tr5 in t_pars.filter(pid=104, name="RT") - assert tr3 not in t_pars.filter(pid=105, name="T") - assert tr5 not in t_pars.filter(pid=105, name="T") + assert tr3 in t_pars.filter_by_identity(pid=101) + assert tr5 not in t_pars.filter_by_identity(pid=101) + assert tr3 not in t_pars.filter_by_identity(name="R") + assert tr5 in t_pars.filter_by_identity(name="R") + assert tr3 in t_pars.filter_by_identity(pid=101, name="R") + assert tr5 not in t_pars.filter_by_identity(pid=101, name="R") + assert tr3 not in t_pars.filter_by_identity(pid=104, name="RT") + assert tr5 in t_pars.filter_by_identity(pid=104, name="RT") + assert tr3 not in t_pars.filter_by_identity(pid=105, name="T") + assert tr5 not in t_pars.filter_by_identity(pid=105, name="T") # Works also without id / name and with duplicate parents for ent in test_ents: t.add_parent(ent) for ent in t_pars: - assert ent in t_pars.filter(ent) + assert ent in t_pars.filter_by_identity(ent) # Grid-Based r7 = Record() r7.add_property(Property()).add_property(name="A").add_property(name="B") r7.add_property(id=27).add_property(id=27, name="A").add_property(id=27, name="B") r7.add_property(id=43).add_property(id=43, name="A").add_property(id=43, name="B") - assert len(r7.properties.filter(pid=27)) == 3 - assert len(r7.properties.filter(pid=43)) == 3 - assert len(r7.properties.filter(pid=43, conjunction=True)) == 3 - assert len(r7.properties.filter(name="A")) == 3 - assert len(r7.properties.filter(name="B")) == 3 - assert len(r7.properties.filter(name="B", conjunction=True)) == 3 - assert len(r7.properties.filter(pid=1, name="A")) == 1 - assert len(r7.properties.filter(pid=1, name="A", conjunction=True)) == 0 - assert len(r7.properties.filter(pid=27, name="B")) == 4 - assert len(r7.properties.filter(pid=27, name="B", conjunction=True)) == 1 - assert len(r7.properties.filter(pid=27, name="C")) == 3 - assert len(r7.properties.filter(pid=27, name="C", conjunction=True)) == 0 + assert len(r7.properties.filter_by_identity(pid=27)) == 3 + assert len(r7.properties.filter_by_identity(pid=43)) == 3 + assert len(r7.properties.filter_by_identity(pid=43, conjunction=True)) == 3 + assert len(r7.properties.filter_by_identity(name="A")) == 3 + assert len(r7.properties.filter_by_identity(name="B")) == 3 + assert len(r7.properties.filter_by_identity(name="B", conjunction=True)) == 3 + assert len(r7.properties.filter_by_identity(pid=1, name="A")) == 1 + assert len(r7.properties.filter_by_identity(pid=1, name="A", conjunction=True)) == 0 + assert len(r7.properties.filter_by_identity(pid=27, name="B")) == 4 + assert len(r7.properties.filter_by_identity(pid=27, name="B", conjunction=True)) == 1 + assert len(r7.properties.filter_by_identity(pid=27, name="C")) == 3 + assert len(r7.properties.filter_by_identity(pid=27, name="C", conjunction=True)) == 0 # Entity based filtering behaves the same - assert (r7.properties.filter(pid=27) == - r7.properties.filter(Property(id=27))) - assert (r7.properties.filter(pid=43, conjunction=True) == - r7.properties.filter(Property(id=43), conjunction=True)) - assert (r7.properties.filter(name="A") == - r7.properties.filter(Property(name="A"))) - assert (r7.properties.filter(name="B") == - r7.properties.filter(Property(name="B"))) - assert (r7.properties.filter(name="B", conjunction=True) == - r7.properties.filter(Property(name="B"), conjunction=True)) - assert (r7.properties.filter(pid=1, name="A") == - r7.properties.filter(Property(id=1, name="A"))) - assert (r7.properties.filter(pid=1, name="A", conjunction=True) == - r7.properties.filter(Property(id=1, name="A"), conjunction=True)) - assert (r7.properties.filter(pid=27, name="B") == - r7.properties.filter(Property(id=27, name="B"))) - assert (r7.properties.filter(pid=27, name="B", conjunction=True) == - r7.properties.filter(Property(id=27, name="B"), conjunction=True)) - assert (r7.properties.filter(pid=27, name="C") == - r7.properties.filter(Property(id=27, name="C"))) - assert (r7.properties.filter(pid=27, name="C", conjunction=True) == - r7.properties.filter(Property(id=27, name="C"), conjunction=True)) + assert (r7.properties.filter_by_identity(pid=27) == + r7.properties.filter_by_identity(Property(id=27))) + assert (r7.properties.filter_by_identity(pid=43, conjunction=True) == + r7.properties.filter_by_identity(Property(id=43), conjunction=True)) + assert (r7.properties.filter_by_identity(name="A") == + r7.properties.filter_by_identity(Property(name="A"))) + assert (r7.properties.filter_by_identity(name="B") == + r7.properties.filter_by_identity(Property(name="B"))) + assert (r7.properties.filter_by_identity(name="B", conjunction=True) == + r7.properties.filter_by_identity(Property(name="B"), conjunction=True)) + assert (r7.properties.filter_by_identity(pid=1, name="A") == + r7.properties.filter_by_identity(Property(id=1, name="A"))) + assert (r7.properties.filter_by_identity(pid=1, name="A", conjunction=True) == + r7.properties.filter_by_identity(Property(id=1, name="A"), conjunction=True)) + assert (r7.properties.filter_by_identity(pid=27, name="B") == + r7.properties.filter_by_identity(Property(id=27, name="B"))) + assert (r7.properties.filter_by_identity(pid=27, name="B", conjunction=True) == + r7.properties.filter_by_identity(Property(id=27, name="B"), conjunction=True)) + assert (r7.properties.filter_by_identity(pid=27, name="C") == + r7.properties.filter_by_identity(Property(id=27, name="C"))) + assert (r7.properties.filter_by_identity(pid=27, name="C", conjunction=True) == + r7.properties.filter_by_identity(Property(id=27, name="C"), conjunction=True)) # Name only matching and name overwrite r8 = Record().add_property(name="A").add_property(name="B").add_property(name="B") r8.add_property(Property(name="A"), name="B") r8.add_property(Property(name="A", id=12), name="C") - assert len(r8.properties.filter(name="A")) == 1 - assert len(r8.properties.filter(name="B")) == 3 - assert len(r8.properties.filter(name="C")) == 1 - assert len(r8.properties.filter(pid=12)) == 1 + assert len(r8.properties.filter_by_identity(name="A")) == 1 + assert len(r8.properties.filter_by_identity(name="B")) == 3 + assert len(r8.properties.filter_by_identity(name="C")) == 1 + assert len(r8.properties.filter_by_identity(pid=12)) == 1 + + + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + + r7.properties.filter(pid=34) + assert issubclass(w[-1].category, DeprecationWarning) + assert "This function was renamed" in str(w[-1].message) + + t.parents.filter(pid=234) + assert issubclass(w[-1].category, DeprecationWarning) + assert "This function was renamed" in str(w[-1].message)