From edfb031a902c233f7090bd5996cf7cd143e9d68a Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sat, 22 Jun 2024 00:31:45 +0200 Subject: [PATCH 1/7] Added get_restriction_dict() --- tripper/triplestore.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/tripper/triplestore.py b/tripper/triplestore.py index c1407c8e..152fcb68 100644 --- a/tripper/triplestore.py +++ b/tripper/triplestore.py @@ -798,6 +798,7 @@ def restrictions( # pylint: disable=redefined-builtin target: "Optional[Union[str, Literal]]" = None, type: "Optional[RestrictionType]" = None, cardinality: "Optional[int]" = None, + return_dicts: bool = False, ) -> "Generator[Triple, None, None]": # pylint: disable=too-many-boolean-expressions """Returns a generator over matching restrictions. @@ -816,6 +817,13 @@ def restrictions( # pylint: disable=redefined-builtin or a literal) cardinality: the cardinality value for cardinality restrictions. + return_dicts: Whether to returned generator is over dicts (see + _get_restriction_dict()). Default is to return a generator + over blank node IRIs. + + Returns: + A generator over matching restrictions. See `return_dicts` + argument for types iterated over. """ if type is None: types = set(self._restriction_types.keys()) @@ -852,7 +860,7 @@ def restrictions( # pylint: disable=redefined-builtin for iri in self.subjects(predicate=OWL.onProperty, object=property): if ( self.has(iri, RDF.type, OWL.Restriction) - and self.has(cls, RDFS.subClassOf, iri) + and (not cls or self.has(cls, RDFS.subClassOf, iri)) and any(self.has(iri, p, target) for p in pred) and ( not card @@ -860,7 +868,31 @@ def restrictions( # pylint: disable=redefined-builtin or any(self.has(iri, c, lcard) for c in card) ) ): - yield iri + yield self._get_restriction_dict(iri) if return_dicts else iri + + def _get_restriction_dict(self, iri): + """Return a dict describing restriction with `iri`. + + The returned dict has the following keys: + - iri: (str) IRI of the restriction itself (blank node). + - cls: (str) IRI of class to which the restriction applies. + - property: (str) IRI of restriction property + - type: (str) One of: "some", "only", "exactly", "min", "max", "value". + - target: (str|Literal) IRI or literal value of the restriction target. + - cardinality: (int) Restriction cardinality (optional). + """ + dct = {p: o for s, p, o in self.triples(iri)} + ((t, p, c),) = [ + (t, p, c) for t, (p, c) in self._restriction_types if p in dct + ] + return { + "iri": iri, + "cls": self.value(predicate=RDFS.subClassOf, object=iri), + "property": dct[OWL.onProperty], + "type": t, + "target": dct[p], + "cardinality": dct.get(c, None), + } def map( self, From f45f12ad86f75614483eab1777fa2117c7aec8bd Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sat, 22 Jun 2024 11:16:38 +0200 Subject: [PATCH 2/7] Added more testing to restrictions() interface --- tests/test_triplestore.py | 72 ++++++++++++++++++++++++++++++++------- tripper/triplestore.py | 65 ++++++++++++++++++++--------------- 2 files changed, 96 insertions(+), 41 deletions(-) diff --git a/tests/test_triplestore.py b/tests/test_triplestore.py index 33910e60..dcfc2cc3 100644 --- a/tests/test_triplestore.py +++ b/tests/test_triplestore.py @@ -95,7 +95,7 @@ def test_triplestore( # pylint: disable=too-many-locals # if True: -def test_restriction() -> None: +def test_restriction() -> None: # pylint: disable=too-many-statements """Test add_restriction() method.""" pytest.importorskip("rdflib") @@ -109,7 +109,7 @@ def test_restriction() -> None: iri = ts.add_restriction( cls=EX.Animal, property=EX.hasPart, - target=EX.Cell, + value=EX.Cell, type="some", ) txt = ts.serialize(format="ntriples") @@ -122,7 +122,7 @@ def test_restriction() -> None: iri2 = ts.add_restriction( cls=EX.Kerberos, property=EX.hasBodyPart, - target=EX.Head, + value=EX.Head, type="exactly", cardinality=3, ) @@ -140,7 +140,7 @@ def test_restriction() -> None: iri3 = ts.add_restriction( cls=EX.Kerberos, property=EX.position, - target=Literal("The port of Hades"), + value=Literal("The port of Hades"), type="value", ) txt3 = ts.serialize(format="ntriples") @@ -155,14 +155,14 @@ def test_restriction() -> None: ts.add_restriction( cls=EX.Kerberos, property=EX.hasBodyPart, - target=EX.Head, + value=EX.Head, type="wrong_type", ) with pytest.raises(ArgumentTypeError): ts.add_restriction( cls=EX.Kerberos, property=EX.hasBodyPart, - target=EX.Head, + value=EX.Head, type="min", ) @@ -177,19 +177,65 @@ def test_restriction() -> None: assert set(ts.restrictions(property=EX.hasBodyPart)) == {iri2} assert set(ts.restrictions(property=EX.hasPart)) == {iri} assert not set(ts.restrictions(property=EX.hasNoPart)) - assert set(ts.restrictions(target=EX.Cell)) == {iri} - assert set(ts.restrictions(target=EX.Head)) == {iri2} - assert not set(ts.restrictions(target=EX.Leg)) - assert set(ts.restrictions(target=EX.Cell, type="some")) == {iri} - assert set(ts.restrictions(target=EX.Head, type="exactly")) == {iri2} - assert set(ts.restrictions(target=Literal("The port of Hades"))) == {iri3} + assert set(ts.restrictions(value=EX.Cell)) == {iri} + assert set(ts.restrictions(value=EX.Head)) == {iri2} + assert not set(ts.restrictions(value=EX.Leg)) + assert set(ts.restrictions(value=EX.Cell, type="some")) == {iri} + assert set(ts.restrictions(value=EX.Head, type="exactly")) == {iri2} + assert set(ts.restrictions(value=Literal("The port of Hades"))) == {iri3} with pytest.raises(ArgumentValueError): - set(ts.restrictions(target=EX.Cell, type="wrong_type")) + set(ts.restrictions(value=EX.Cell, type="wrong_type")) with pytest.raises(ArgumentValueError): set(ts.restrictions(type="value", cardinality=2)) + # Test return_dicts + dicts = sorted( + ts.restrictions(return_dicts=True), key=lambda d: d["value"] + ) + for d in dicts: # Remove iri keys, since they refer to blank nodes + d.pop("iri") + assert dicts == sorted( + [ + { + "cls": "http://example.com/onto#Animal", + "property": "http://example.com/onto#hasPart", + "type": "some", + "value": "http://example.com/onto#Cell", + "cardinality": None, + }, + { + "cls": "http://example.com/onto#Kerberos", + "property": "http://example.com/onto#hasBodyPart", + "type": "exactly", + "value": "http://example.com/onto#Head", + "cardinality": Literal("3", datatype=XSD.nonNegativeInteger), + }, + { + "cls": "http://example.com/onto#Kerberos", + "property": "http://example.com/onto#position", + "type": "value", + "value": Literal("The port of Hades", datatype=XSD.string), + "cardinality": None, + }, + ], + key=lambda d: d["value"], + ) + + dicts = list(ts.restrictions(type="some", return_dicts=True)) + for d in dicts: # Remove iri keys, since they refer to blank nodes + d.pop("iri") + assert dicts == [ + { + "cls": "http://example.com/onto#Animal", + "property": "http://example.com/onto#hasPart", + "type": "some", + "value": "http://example.com/onto#Cell", + "cardinality": None, + }, + ] + def test_backend_rdflib(expected_function_triplestore: str) -> None: """Specifically test the rdflib backend Triplestore. diff --git a/tripper/triplestore.py b/tripper/triplestore.py index 152fcb68..f904524e 100644 --- a/tripper/triplestore.py +++ b/tripper/triplestore.py @@ -727,7 +727,7 @@ def add_restriction( # pylint: disable=redefined-builtin self, cls: str, property: str, - target: "Union[str, Literal]", + value: "Union[str, Literal]", type: "RestrictionType", cardinality: "Optional[int]" = None, hashlength: int = 16, @@ -737,14 +737,14 @@ def add_restriction( # pylint: disable=redefined-builtin Parameters: cls: IRI of class to which the restriction applies. property: IRI of restriction property. - target: The IRI or literal value of the restriction target. + value: The IRI or literal value of the restriction target. type: The type of the restriction. Should be one of: - - some: existential restriction (target is a class IRI) - - only: universal restriction (target is a class IRI) - - exactly: cardinality restriction (target is a class IRI) - - min: minimum cardinality restriction (target is a class IRI) - - max: maximum cardinality restriction (target is a class IRI) - - value: Value restriction (target is an IRI of an individual + - some: existential restriction (value is a class IRI) + - only: universal restriction (value is a class IRI) + - exactly: cardinality restriction (value is a class IRI) + - min: minimum cardinality restriction (value is a class IRI) + - max: maximum cardinality restriction (value is a class IRI) + - value: Value restriction (value is an IRI of an individual or a literal) cardinality: the cardinality value for cardinality restrictions. @@ -755,7 +755,7 @@ def add_restriction( # pylint: disable=redefined-builtin """ iri = bnode_iri( prefix="restriction", - source=f"{cls} {property} {target} {type} {cardinality}", + source=f"{cls} {property} {value} {type} {cardinality}", length=hashlength, ) triples = [ @@ -769,7 +769,7 @@ def add_restriction( # pylint: disable=redefined-builtin '"max" or "value"' ) pred, card = self._restriction_types[type] - triples.append((iri, pred, target)) + triples.append((iri, pred, value)) if card: if not cardinality: raise ArgumentTypeError( @@ -795,7 +795,7 @@ def restrictions( # pylint: disable=redefined-builtin self, cls: "Optional[str]" = None, property: "Optional[str]" = None, - target: "Optional[Union[str, Literal]]" = None, + value: "Optional[Union[str, Literal]]" = None, type: "Optional[RestrictionType]" = None, cardinality: "Optional[int]" = None, return_dicts: bool = False, @@ -806,14 +806,14 @@ def restrictions( # pylint: disable=redefined-builtin Parameters: cls: IRI of class to which the restriction applies. property: IRI of restriction property. - target: The IRI or literal value of the restriction target. + value: The IRI or literal value of the restriction target. type: The type of the restriction. Should be one of: - - some: existential restriction (target is a class IRI) - - only: universal restriction (target is a class IRI) - - exactly: cardinality restriction (target is a class IRI) - - min: minimum cardinality restriction (target is a class IRI) - - max: maximum cardinality restriction (target is a class IRI) - - value: Value restriction (target is an IRI of an individual + - some: existential restriction (value is a class IRI) + - only: universal restriction (value is a class IRI) + - exactly: cardinality restriction (value is a class IRI) + - min: minimum cardinality restriction (value is a class IRI) + - max: maximum cardinality restriction (value is a class IRI) + - value: Value restriction (value is an IRI of an individual or a literal) cardinality: the cardinality value for cardinality restrictions. @@ -835,16 +835,16 @@ def restrictions( # pylint: disable=redefined-builtin else: types = {type} if isinstance(type, str) else set(type) - if isinstance(target, Literal): + if isinstance(value, Literal): types.intersection_update({"value"}) - elif isinstance(target, str): + elif isinstance(value, str): types.difference_update({"value"}) if cardinality: types.intersection_update({"exactly", "min", "max"}) if not types: raise ArgumentValueError( - f"Inconsistent type='{type}', target='{target}' and " + f"Inconsistent type='{type}', value='{value}' and " f"cardinality='{cardinality}' arguments" ) pred = {self._restriction_types[t][0] for t in types} @@ -861,7 +861,7 @@ def restrictions( # pylint: disable=redefined-builtin if ( self.has(iri, RDF.type, OWL.Restriction) and (not cls or self.has(cls, RDFS.subClassOf, iri)) - and any(self.has(iri, p, target) for p in pred) + and any(self.has(iri, p, value) for p in pred) and ( not card or not cardinality @@ -878,19 +878,28 @@ def _get_restriction_dict(self, iri): - cls: (str) IRI of class to which the restriction applies. - property: (str) IRI of restriction property - type: (str) One of: "some", "only", "exactly", "min", "max", "value". - - target: (str|Literal) IRI or literal value of the restriction target. + - value: (str|Literal) IRI or literal value of the restriction target. - cardinality: (int) Restriction cardinality (optional). """ - dct = {p: o for s, p, o in self.triples(iri)} - ((t, p, c),) = [ - (t, p, c) for t, (p, c) in self._restriction_types if p in dct - ] + dct = dict(self.predicate_objects(iri)) + if OWL.onClass in dct: + ((t, p, c),) = [ + (t, p, c) + for t, (p, c) in self._restriction_types.items() + if c in dct + ] + else: + ((t, p, c),) = [ + (t, p, c) + for t, (p, c) in self._restriction_types.items() + if p in dct + ] return { "iri": iri, "cls": self.value(predicate=RDFS.subClassOf, object=iri), "property": dct[OWL.onProperty], "type": t, - "target": dct[p], + "value": dct[p], "cardinality": dct.get(c, None), } From 30c5153f0fca18682b9b32ed34c301b9396d5f48 Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sat, 22 Jun 2024 16:07:04 +0200 Subject: [PATCH 3/7] Added a sub-section about restrictions to the tutorial. --- docs/tutorial.md | 63 +++++++++++++++++++++++++++++++++++++++ tests/test_triplestore.py | 2 +- tripper/triplestore.py | 11 ++----- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index 1217166a..c8c44b4c 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -186,6 +186,66 @@ True ``` +### Class restrictions +When working with OWL ontologies, you often need to look up or add class restrictions. +A [restriction] restricts a class to only those individuals that satisfy the restriction. +The Triplestore class has two convenient methods for this, that don't require knowledge about how restrictions are represented in RDF. +They only support basic restrictions without any nested logical constructs. +For more advanced restrictions, we recommend to use [EMMOntoPy] or [Owlready2]. + +A restriction is described by the following set of parameters. + + * **cls**: IRI of class to which the restriction applies. + * **property**: IRI of restriction property. + * **type**: The type of the restriction. Should be one of: + - *some*: existential restriction (target is a class IRI) + - *only*: universal restriction (target is a class IRI) + - *exactly*: cardinality restriction (target is a class IRI) + - *min*: minimum cardinality restriction (target is a class IRI) + - *max*: maximum cardinality restriction (target is a class IRI) + - *value*: Value restriction (target is an IRI of an individual or a literal) + + * **cardinality**: the cardinality value for cardinality restrictions. + * **value**: The IRI or literal value of the restriction target. + +For example. Lets assume that you have a class `onto:Bacteria` that you want to logically restrict to be unicellular. +In Manchester syntax, this can be stated as `onto:Bacteria emmo:hasPart exactly 1 onto:Cell`. +With tripper, you can state it with + +```python +>>> iri = ts.add_restriction( +... cls=ONTO.Bacteria, +... property=EMMO.hasPart, +... type="exactly", +... cardinality=1, +... value=ONTO.Cell, +... ) + +``` +The returned `iri` is the blank node IRI of the new restriction. + + +To find the above restriction, you can use the `restrictions()` method. +It returns an iterator over all restrictions that matches the provided criteria. +For example: + +```python +>>> g = ts.restrictions(cls=ONTO.Bacteria, property=EMMO.hasPart, return_dicts=True) +>>> list(g) # doctest: +ELLIPSIS +[{'iri': '_:...', 'cls': 'http://example.com/onto#Bacteria', 'property': 'https://w3id.org/emmo#EMMO_17e27c22_37e1_468c_9dd7_95e137f73e7f', 'type': 'exactly', 'cardinality': 1, 'value': 'http://example.com/onto#Cell'}] + +``` + +With the `return_dicts` argument set to false, you will instead get an iterator over the IRIs of all matching restrictions: + +```python +>>> g = ts.restrictions(cls=ONTO.Bacteria, property=EMMO.hasPart, return_dicts=False) +>>> list(g) # doctest: +ELLIPSIS +['_:...'] + +``` + + ### Utilities *Todo: Describe the `tripper.utils` module* @@ -272,3 +332,6 @@ https://emmc-asbl.github.io/tripper/latest/api_reference/literal/#tripper.litera [EMMO]: https://emmc.eu/emmo/ [Function Ontology (FnO)]: https://fno.io/ [list of currently supported backends]: https://github.com/EMMC-ASBL/tripper?tab=readme-ov-file#available-backends +[EMMOntoPy]: https://emmo-repo.github.io/EMMOntoPy/ +[Owlready2]: https://pypi.org/project/owlready2/ +[restriction]: https://www.w3.org/TR/owl-ref/#Restriction diff --git a/tests/test_triplestore.py b/tests/test_triplestore.py index dcfc2cc3..d2660264 100644 --- a/tests/test_triplestore.py +++ b/tests/test_triplestore.py @@ -210,7 +210,7 @@ def test_restriction() -> None: # pylint: disable=too-many-statements "property": "http://example.com/onto#hasBodyPart", "type": "exactly", "value": "http://example.com/onto#Head", - "cardinality": Literal("3", datatype=XSD.nonNegativeInteger), + "cardinality": 3, }, { "cls": "http://example.com/onto#Kerberos", diff --git a/tripper/triplestore.py b/tripper/triplestore.py index f904524e..2ede6615 100644 --- a/tripper/triplestore.py +++ b/tripper/triplestore.py @@ -721,8 +721,6 @@ def prefix_iri(self, iri: str, require_prefixed: bool = False): "value": (OWL.hasValue, None), } - # xtype: "TypeLiteral['some', 'only', 'exactly', 'min', 'max', 'value']", - def add_restriction( # pylint: disable=redefined-builtin self, cls: str, @@ -786,11 +784,6 @@ def add_restriction( # pylint: disable=redefined-builtin self.add_triples(triples) return iri - # xtype: ( - # "Optional[TypeLiteral['some', 'only', 'exactly', 'min', " - # "'max', 'value']]" - # ) = None, - def restrictions( # pylint: disable=redefined-builtin self, cls: "Optional[str]" = None, @@ -878,8 +871,8 @@ def _get_restriction_dict(self, iri): - cls: (str) IRI of class to which the restriction applies. - property: (str) IRI of restriction property - type: (str) One of: "some", "only", "exactly", "min", "max", "value". - - value: (str|Literal) IRI or literal value of the restriction target. - cardinality: (int) Restriction cardinality (optional). + - value: (str|Literal) IRI or literal value of the restriction target. """ dct = dict(self.predicate_objects(iri)) if OWL.onClass in dct: @@ -899,8 +892,8 @@ def _get_restriction_dict(self, iri): "cls": self.value(predicate=RDFS.subClassOf, object=iri), "property": dct[OWL.onProperty], "type": t, + "cardinality": int(dct[c]) if c else None, "value": dct[p], - "cardinality": dct.get(c, None), } def map( From 95f1d32679b34869dc0f5ec26872ad647ecf10de Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sat, 22 Jun 2024 16:12:04 +0200 Subject: [PATCH 4/7] Updated tutorial --- docs/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index c8c44b4c..a27aec2f 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -240,8 +240,8 @@ With the `return_dicts` argument set to false, you will instead get an iterator ```python >>> g = ts.restrictions(cls=ONTO.Bacteria, property=EMMO.hasPart, return_dicts=False) ->>> list(g) # doctest: +ELLIPSIS -['_:...'] +>>> next(g) == iri +True ``` From 331322c3d46cbeec9239274253da13f33aff00ea Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Mon, 24 Jun 2024 09:47:16 +0200 Subject: [PATCH 5/7] Apply suggestions from code review Co-authored-by: Francesca L. Bleken <48128015+francescalb@users.noreply.github.com> --- docs/tutorial.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index a27aec2f..eaa952f6 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -187,13 +187,12 @@ True ``` ### Class restrictions -When working with OWL ontologies, you often need to look up or add class restrictions. -A [restriction] restricts a class to only those individuals that satisfy the restriction. -The Triplestore class has two convenient methods for this, that don't require knowledge about how restrictions are represented in RDF. -They only support basic restrictions without any nested logical constructs. +When working with OWL ontologies, it is often required to inspect or add class [restriction]s. +The Triplestore class has two convenient methods for this, that do not require knowledge about how restrictions are represented in RDF. +Only support basic restrictions, without any nested logical constructs, are supoprted. For more advanced restrictions, we recommend to use [EMMOntoPy] or [Owlready2]. -A restriction is described by the following set of parameters. +A [restriction] is described by the following set of parameters. * **cls**: IRI of class to which the restriction applies. * **property**: IRI of restriction property. @@ -208,9 +207,9 @@ A restriction is described by the following set of parameters. * **cardinality**: the cardinality value for cardinality restrictions. * **value**: The IRI or literal value of the restriction target. -For example. Lets assume that you have a class `onto:Bacteria` that you want to logically restrict to be unicellular. +As an example, the class `onto:Bacteria` can be logically restricted to be unicellular. In Manchester syntax, this can be stated as `onto:Bacteria emmo:hasPart exactly 1 onto:Cell`. -With tripper, you can state it with +With Tripper this can be stated as: ```python >>> iri = ts.add_restriction( @@ -225,7 +224,7 @@ With tripper, you can state it with The returned `iri` is the blank node IRI of the new restriction. -To find the above restriction, you can use the `restrictions()` method. +To find the above restriction, the `restrictions()` method can be used. It returns an iterator over all restrictions that matches the provided criteria. For example: @@ -236,7 +235,7 @@ For example: ``` -With the `return_dicts` argument set to false, you will instead get an iterator over the IRIs of all matching restrictions: +With the `return_dicts` argument set to false, an iterator over the IRIs of all matching restrictions is returned: ```python >>> g = ts.restrictions(cls=ONTO.Bacteria, property=EMMO.hasPart, return_dicts=False) From 486b60aecd6274c822a1820529318c329f6b6d3d Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Mon, 24 Jun 2024 10:22:55 +0200 Subject: [PATCH 6/7] Renamed the `return_dicts` argument of restrictions() to `asdict` and changed the default to True. --- tests/test_triplestore.py | 52 +++++++++++++++++++++++---------------- tripper/triplestore.py | 10 ++++---- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/tests/test_triplestore.py b/tests/test_triplestore.py index 98ff8139..bd006111 100644 --- a/tests/test_triplestore.py +++ b/tests/test_triplestore.py @@ -167,22 +167,34 @@ def test_restriction() -> None: # pylint: disable=too-many-statements ) # Test find restriction - assert set(ts.restrictions()) == {iri, iri2, iri3} - assert set(ts.restrictions(cls=EX.Kerberos)) == {iri2, iri3} - assert set(ts.restrictions(cls=EX.Animal)) == {iri} - assert not set(ts.restrictions(cls=EX.Dog)) - assert set(ts.restrictions(cls=EX.Kerberos, cardinality=3)) == {iri2} - assert set(ts.restrictions(cardinality=3)) == {iri2} - assert not set(ts.restrictions(cls=EX.Kerberos, cardinality=2)) - assert set(ts.restrictions(property=EX.hasBodyPart)) == {iri2} - assert set(ts.restrictions(property=EX.hasPart)) == {iri} - assert not set(ts.restrictions(property=EX.hasNoPart)) - assert set(ts.restrictions(value=EX.Cell)) == {iri} - assert set(ts.restrictions(value=EX.Head)) == {iri2} - assert not set(ts.restrictions(value=EX.Leg)) - assert set(ts.restrictions(value=EX.Cell, type="some")) == {iri} - assert set(ts.restrictions(value=EX.Head, type="exactly")) == {iri2} - assert set(ts.restrictions(value=Literal("The port of Hades"))) == {iri3} + assert set(ts.restrictions(asdict=False)) == {iri, iri2, iri3} + assert set(ts.restrictions(cls=EX.Kerberos, asdict=False)) == {iri2, iri3} + assert set(ts.restrictions(cls=EX.Animal, asdict=False)) == {iri} + assert not set(ts.restrictions(cls=EX.Dog, asdict=False)) + assert set( + ts.restrictions(cls=EX.Kerberos, cardinality=3, asdict=False) + ) == {iri2} + assert set(ts.restrictions(cardinality=3, asdict=False)) == {iri2} + assert not set( + ts.restrictions(cls=EX.Kerberos, cardinality=2, asdict=False) + ) + assert set(ts.restrictions(property=EX.hasBodyPart, asdict=False)) == { + iri2 + } + assert set(ts.restrictions(property=EX.hasPart, asdict=False)) == {iri} + assert not set(ts.restrictions(property=EX.hasNoPart, asdict=False)) + assert set(ts.restrictions(value=EX.Cell, asdict=False)) == {iri} + assert set(ts.restrictions(value=EX.Head, asdict=False)) == {iri2} + assert not set(ts.restrictions(value=EX.Leg, asdict=False)) + assert set(ts.restrictions(value=EX.Cell, type="some", asdict=False)) == { + iri + } + assert set( + ts.restrictions(value=EX.Head, type="exactly", asdict=False) + ) == {iri2} + assert set( + ts.restrictions(value=Literal("The port of Hades"), asdict=False) + ) == {iri3} with pytest.raises(ArgumentValueError): set(ts.restrictions(value=EX.Cell, type="wrong_type")) @@ -190,10 +202,8 @@ def test_restriction() -> None: # pylint: disable=too-many-statements with pytest.raises(ArgumentValueError): set(ts.restrictions(type="value", cardinality=2)) - # Test return_dicts - dicts = sorted( - ts.restrictions(return_dicts=True), key=lambda d: d["value"] - ) + # Test returning as dicts (asdict=True) + dicts = sorted(ts.restrictions(asdict=True), key=lambda d: d["value"]) for d in dicts: # Remove iri keys, since they refer to blank nodes d.pop("iri") assert dicts == sorted( @@ -223,7 +233,7 @@ def test_restriction() -> None: # pylint: disable=too-many-statements key=lambda d: d["value"], ) - dicts = list(ts.restrictions(type="some", return_dicts=True)) + dicts = list(ts.restrictions(type="some", asdict=True)) for d in dicts: # Remove iri keys, since they refer to blank nodes d.pop("iri") assert dicts == [ diff --git a/tripper/triplestore.py b/tripper/triplestore.py index 7bb885c8..e5180088 100644 --- a/tripper/triplestore.py +++ b/tripper/triplestore.py @@ -799,7 +799,7 @@ def restrictions( # pylint: disable=redefined-builtin value: "Optional[Union[str, Literal]]" = None, type: "Optional[RestrictionType]" = None, cardinality: "Optional[int]" = None, - return_dicts: bool = False, + asdict: bool = True, ) -> "Generator[Triple, None, None]": # pylint: disable=too-many-boolean-expressions """Returns a generator over matching restrictions. @@ -818,13 +818,13 @@ def restrictions( # pylint: disable=redefined-builtin or a literal) cardinality: the cardinality value for cardinality restrictions. - return_dicts: Whether to returned generator is over dicts (see + asdict: Whether to returned generator is over dicts (see _get_restriction_dict()). Default is to return a generator over blank node IRIs. Returns: - A generator over matching restrictions. See `return_dicts` - argument for types iterated over. + A generator over matching restrictions. See `asdict` argument + for types iterated over. """ if type is None: types = set(self._restriction_types.keys()) @@ -869,7 +869,7 @@ def restrictions( # pylint: disable=redefined-builtin or any(self.has(iri, c, lcard) for c in card) ) ): - yield self._get_restriction_dict(iri) if return_dicts else iri + yield self._get_restriction_dict(iri) if asdict else iri def _get_restriction_dict(self, iri): """Return a dict describing restriction with `iri`. From d0a205e9618ce86324f25f64def260e1aebfd172 Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Mon, 24 Jun 2024 10:32:50 +0200 Subject: [PATCH 7/7] Also updated the tutorial to use the new asdict argument of restrictions() method. --- docs/tutorial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial.md b/docs/tutorial.md index eaa952f6..e0f81fd2 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -229,7 +229,7 @@ It returns an iterator over all restrictions that matches the provided criteria. For example: ```python ->>> g = ts.restrictions(cls=ONTO.Bacteria, property=EMMO.hasPart, return_dicts=True) +>>> g = ts.restrictions(cls=ONTO.Bacteria, property=EMMO.hasPart, asdict=True) >>> list(g) # doctest: +ELLIPSIS [{'iri': '_:...', 'cls': 'http://example.com/onto#Bacteria', 'property': 'https://w3id.org/emmo#EMMO_17e27c22_37e1_468c_9dd7_95e137f73e7f', 'type': 'exactly', 'cardinality': 1, 'value': 'http://example.com/onto#Cell'}] @@ -238,7 +238,7 @@ For example: With the `return_dicts` argument set to false, an iterator over the IRIs of all matching restrictions is returned: ```python ->>> g = ts.restrictions(cls=ONTO.Bacteria, property=EMMO.hasPart, return_dicts=False) +>>> g = ts.restrictions(cls=ONTO.Bacteria, property=EMMO.hasPart, asdict=False) >>> next(g) == iri True