diff --git a/ontopy/graph.py b/ontopy/graph.py index b3f77958b..0ad9e578a 100644 --- a/ontopy/graph.py +++ b/ontopy/graph.py @@ -678,7 +678,7 @@ def get_node_attrs(self, name, nodeattrs, attrs): label = get_label(entity) # class if isinstance(entity, owlready2.ThingClass): - if self.ontology.is_defined(entity): + if entity.is_defined: kwargs = self.style.get("defined_class", {}) else: kwargs = self.style.get("class", {}) diff --git a/ontopy/ontology.py b/ontopy/ontology.py index 04b50712f..de97ce424 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -1363,7 +1363,17 @@ def is_individual(self, entity): # FIXME - deprecate this method as soon the ThingClass property # `defined_class` works correct in Owlready2 def is_defined(self, entity): - """Returns true if the entity is a defined class.""" + """Returns true if the entity is a defined class. + + Deprecated, use the `is_defined` property of the classes + (ThingClass subclasses) instead. + """ + warnings.warn( + "This method is deprecated. Use the `is_defined` property of " + "the classes instad.", + DeprecationWarning, + stacklevel=2, + ) if isinstance(entity, str): entity = self.get_by_label(entity) return hasattr(entity, "equivalent_to") and bool(entity.equivalent_to) diff --git a/ontopy/patch.py b/ontopy/patch.py index 6a957e2cc..db8b37698 100644 --- a/ontopy/patch.py +++ b/ontopy/patch.py @@ -136,6 +136,23 @@ def get_indirect_is_a(self, skip_classes=True): return subclass_relations +is_defined = property( + fget=lambda self: ( + hasattr(self, "equivalent_to") and bool(self.equivalent_to) + ), + doc="""Is true if this class is a defined class. + + For a "defined class" both necessary and sufficient conditions for + membership in that class are given. Hence, classes declared with + `owl:equivalentTo` are defined classes. + + Note that this method is different from the `defined_class` + property provided by Owlready2, who's boolean value is set by the + user. + """, +) + + # Inject methods into ThingClass setattr(ThingClass, "__dir__", _dir) setattr(ThingClass, "get_preferred_label", get_preferred_label) @@ -143,6 +160,7 @@ def get_indirect_is_a(self, skip_classes=True): setattr(ThingClass, "get_annotations", get_annotations) setattr(ThingClass, "disjoint_with", disjoint_with) setattr(ThingClass, "get_indirect_is_a", get_indirect_is_a) +setattr(ThingClass, "is_defined", is_defined) # diff --git a/requirements_dev.txt b/requirements_dev.txt index 5835233d6..45ebc23e9 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,5 +1,5 @@ pre-commit>=2.21.0,<3; python_version<"3.8" -pre-commit~=3.2; python_version>="3.8" +pre-commit~=3.3; python_version>="3.8" pylint~=2.17 pytest~=7.3 pytest-cov~=4.0 diff --git a/tests/ontopy_tests/test_patch.py b/tests/ontopy_tests/test_patch.py new file mode 100644 index 000000000..5d86dcc58 --- /dev/null +++ b/tests/ontopy_tests/test_patch.py @@ -0,0 +1,61 @@ +"""Tests Owlready2 patches implemented in ontopy/patch.py + +Implemented as a script, such that it easy to understand and use for debugging. +""" +from ontopy import get_ontology + +from owlready2 import owl, Inverse + + +emmo = get_ontology().load() + + +# Test some ThingClass extensions implemented in patch.py +assert emmo.Atom.get_preferred_label() == "Atom" + +assert emmo.Atom.get_parents() == { + emmo.CausalSystem, + emmo.CompositeParticle, + emmo.MolecularEntity, +} + +assert set(emmo.Atom.get_annotations().keys()) == { + "prefLabel", + "altLabel", + "elucidation", +} + +# TODO: Fix disjoint_with(). +# It seems not to take into account disjoint unions. +# assert set(emmo.Collection.disjoint_with()) == {emmo.Item} + +assert set(str(s) for s in emmo.CausalChain.get_indirect_is_a()) == set( + str(s) + for s in { + Inverse(emmo.hasPart).value(emmo.universe), + emmo.CausalObject, + emmo.Particle, + emmo.hasPart.some(emmo.Quantum), + emmo.hasTemporalPart.only(emmo.CausalChain | emmo.Quantum), + emmo.hasTemporalPart.some(emmo.CausalChain | emmo.Quantum), + } +) +assert set( + str(s) for s in emmo.CausalChain.get_indirect_is_a(skip_classes=False) +) == set( + str(s) + for s in { + Inverse(emmo.hasPart).value(emmo.universe), + emmo.CausalObject, + emmo.EMMO, + emmo.Item, + emmo.Particle, + emmo.hasPart.some(emmo.Quantum), + emmo.hasTemporalPart.only(emmo.CausalChain | emmo.Quantum), + emmo.hasTemporalPart.some(emmo.CausalChain | emmo.Quantum), + owl.Thing, + } +) + +assert emmo.Atom.is_defined == False +assert emmo.Holistic.is_defined == True