Skip to content

Commit

Permalink
Update to comply with owlready2>0.41 (#639)
Browse files Browse the repository at this point in the history
* Update to comply with owlready2>0.41 which returns a owlready2.locstr
instead of str


* Updated excelparser to use emmo1.0.0-beta4, patch and excelparser OK.

* _getattr monkey patch so that ThingClass annotations can be directly accessed.

* Added some get_by_albel_tests that codecov is complaining about.

* Added wu-palmer test in test_generations
  • Loading branch information
francescalb authored Oct 9, 2023
1 parent 8f971bd commit 24b86ad
Show file tree
Hide file tree
Showing 22 changed files with 236 additions and 169 deletions.
6 changes: 1 addition & 5 deletions ontopy/excelparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,10 @@ def create_ontology_from_pandas( # pylint:disable=too-many-locals,too-many-bran
# Set given or default base_iri if base_iri_from_metadata is False.
if not base_iri_from_metadata:
onto.base_iri = base_iri

onto.sync_python_names()
# onto.sync_python_names()
# prefLabel, label, and altLabel
# are default label annotations
onto.set_default_label_annotations()

# Add object properties
if objectproperties is not None:
objectproperties = _clean_dataframe(objectproperties)
Expand Down Expand Up @@ -367,7 +365,6 @@ def create_ontology_from_pandas( # pylint:disable=too-many-locals,too-many-bran
onto.sync_attributes(
name_policy="uuid", name_prefix="EMMO_", class_docstring="elucidation"
)

# Clean up data frame with new concepts
data = _clean_dataframe(data)
# Add entities
Expand Down Expand Up @@ -562,7 +559,6 @@ def get_metadata_from_dataframe( # pylint: disable=too-many-locals,too-many-bra
)
except AttributeError:
pass

return onto, catalog


Expand Down
6 changes: 3 additions & 3 deletions ontopy/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,11 +720,11 @@ def _relation_styles(
"""
for relation in entity.mro():
if relation in rels:
if get_label(relation) in relations:
rattrs = relations[get_label(relation)]
if str(get_label(relation)) in relations:
rattrs = relations[str(get_label(relation))]
else:
for alt_label in relation.get_annotations()["altLabel"]:
rattrs = relations[alt_label]
rattrs = relations[str(alt_label)]

break
else:
Expand Down
1 change: 0 additions & 1 deletion ontopy/ontology.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,6 @@ def save(
)

revmap = {value: key for key, value in FMAP.items()}

if filename is None:
if format:
fmt = revmap.get(format, format)
Expand Down
38 changes: 32 additions & 6 deletions ontopy/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from owlready2 import AnnotationPropertyClass, ThingClass, PropertyClass
from owlready2 import Metadata, Thing, Restriction, Namespace
from ontopy.utils import EMMOntoPyException
from ontopy.ontology import Ontology as OntopyOntology


def render_func(entity):
Expand All @@ -27,6 +28,10 @@ def render_func(entity):
#
# Extending ThingClass (classes)
# ==============================

save_getattr = ThingClass.__getattr__


def get_preferred_label(self):
"""Returns the preferred label as a string (not list).
Expand Down Expand Up @@ -69,7 +74,7 @@ def get_parents(self, strict=False):
def _dir(self):
"""Extend dir() listing of ontology classes."""
set_dir = set(object.__dir__(self))
props = self.namespace.world._props.keys()
props = [str(key) for key in self.namespace.world._props.keys()]
set_dir.update(props)
return sorted(set_dir)

Expand All @@ -91,14 +96,16 @@ def _setitem(self, name, value):
Example:
>>> from emmopy import get_emmo
>>> from owlready2 import locstr
>>> emmo = get_emmo()
>>> emmo.Atom['altLabel']
['ChemicalElement']
[locstr('ChemicalElement', 'en')]
>>> emmo.Atom['altLabel'] = 'Element'
>>> emmo.Atom['altLabel'] = locstr('Atomo', 'it')
>>> emmo.Atom['altLabel']
['ChemicalElement', 'Element']
[locstr('ChemicalElement', 'en'), 'Element', locstr('Atomo', 'it')]
"""

item = _getitem(self, name)
item.append(value)

Expand All @@ -112,6 +119,24 @@ def _delitem(self, name):
item.clear()


def _getattr(self, name):
"""Provide attribute access to annotation properties.
This upates __getattr__ in owlready2. If name is not found as
attribute it tries using the iriname of the annotation property.
"""
try:
return save_getattr(self, name)
except AttributeError as err:
# make sure we are using and ontopy Ontology which has get_by_label
if isinstance(self.namespace.ontology, OntopyOntology):
entity = self.namespace.ontology.get_by_label(name)
# add annotation property to world._props for faster access later
self.namespace.world._props[name] = entity
return save_getattr(self, entity.name)
raise err


def get_annotations(
self, all=False, imported=True
): # pylint: disable=redefined-builtin
Expand All @@ -125,7 +150,7 @@ def get_annotations(
onto = self.namespace.ontology

annotations = {
get_preferred_label(_): _._get_values_for_class(self)
str(get_preferred_label(_)): _._get_values_for_class(self)
for _ in onto.annotation_properties(imported=imported)
}
if all:
Expand Down Expand Up @@ -197,6 +222,7 @@ def get_indirect_is_a(self, skip_classes=True):
setattr(ThingClass, "__getitem__", _getitem)
setattr(ThingClass, "__setitem__", _setitem)
setattr(ThingClass, "__delitem__", _delitem)
setattr(ThingClass, "__getattr__", _getattr)
setattr(ThingClass, "get_preferred_label", get_preferred_label)
setattr(ThingClass, "get_parents", get_parents)
setattr(ThingClass, "get_annotations", get_annotations)
Expand Down Expand Up @@ -252,7 +278,7 @@ def namespace_init(self, world_or_ontology, base_iri, name=None):
# Extending Metadata
# ==================
def keys(self):
"""Return a generator over annotation property names associates
"""Return a generator over annotation property names associated
with this ontology."""
namespace = self.namespace
for annotation in namespace.annotation_properties():
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defusedxml>=0.7.1,<1
graphviz>=0.16,<0.21
numpy>=1.19.5,<2
openpyxl>=3.0.9,<3.2
Owlready2>=0.28,!=0.32,!=0.34,<0.42
Owlready2>=0.28,!=0.32,!=0.34,<0.44
packaging>=21.0,<24
pandas>=1.2,<2.1
Pygments>=2.7.4,<3
Expand Down
14 changes: 13 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

if TYPE_CHECKING:
from ontopy.ontology import Ontology
from typing import Sequence
import sys, os

# Add test-specific utilities to path
sys.path.append(os.path.join(os.path.dirname(__file__), "utilities"))


@pytest.fixture(scope="session")
Expand All @@ -16,7 +21,14 @@ def repo_dir() -> Path:

@pytest.fixture
def emmo() -> "Ontology":
"""Load and return EMMO."""
"""Load and return EMMO.
Note that this loads the version of EMMO that is
the current defailt in ontopy.
When updating the defeault EMMO for ontopy,
tests might need to be updated.
"""
from emmopy import get_emmo

emmo = get_emmo()
Expand Down
40 changes: 0 additions & 40 deletions tests/ontopy_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,3 @@

# Files to skip
collect_ignore = ["interactive_test.py"]


# Utilities
def abbreviate(onto, iri, must_exist=True):
"""Returns existing Owlready2 storid for `iri`."""
if iri is None:
return None
abbreviater = getattr(onto, "_abbreviate")
storid = abbreviater(iri, create_if_missing=False)
if storid is None and must_exist:
raise ValueError(f"no such IRI in ontology: {iri}")
return storid


def get_triples(onto, s=None, p=None, o=None) -> list:
"""Returns a list of triples matching spo."""
return [
(
onto._unabbreviate(s_) if isinstance(s_, int) and s_ > 0 else s_,
onto._unabbreviate(p_) if isinstance(p_, int) and p_ > 0 else p_,
onto._unabbreviate(o_) if isinstance(o_, int) and o_ > 0 else o_,
)
for s_, p_, o_, d in onto._get_triples_spod_spod(
abbreviate(onto, s),
abbreviate(onto, p),
abbreviate(onto, o, False) or o,
None,
)
]


def has_triple(onto, s=None, p=None, o=None) -> bool:
"""Returns true if ontology `onto` contains the given triple.
None may be used as a wildcard for of `s`, `p` or `o`.
"""
try:
return bool(get_triples(onto, s, p, o))
except ValueError:
return False
113 changes: 78 additions & 35 deletions tests/ontopy_tests/test_patch.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,91 @@
"""Tests Owlready2 patches implemented in ontopy/patch.py
Implemented as a script, such that it easy to understand and use for debugging.
"""

import pytest

from ontopy import get_ontology

from owlready2 import owl, Inverse

from utilities import setassert


def test_get_by_label_onto(emmo: "Ontology") -> None:
# Test some ThingClass extensions implemented in patch.py
assert str(emmo.Atom.get_preferred_label()) == "Atom"

assert emmo.Atom.get_parents() == {emmo.MolecularEntity}

setassert(
emmo.Atom.get_annotations().keys(),
{
"prefLabel",
"altLabel",
"elucidation",
"comment",
},
)
setassert(
emmo.Atom.get_annotations(all=True).keys(),
{
"qualifiedCardinality",
"minQualifiedCardinality",
"prefLabel",
"abstract",
"hiddenLabel",
"etymology",
"altLabel",
"example",
"elucidation",
"OWLDLRestrictedAxiom",
"wikipediaReference",
"conceptualisation",
"logo",
"comment",
"dbpediaReference",
"definition",
"VIMTerm",
"creator",
"iupacReference",
"contact",
"omReference",
"ISO9000Reference",
"ISO80000Reference",
"qudtReference",
"contributor",
"license",
"ISO14040Reference",
"figure",
"title",
"publisher",
},
)

# Test item access/assignment/deletion for classes
setassert(emmo.Atom["altLabel"], {"ChemicalElement"})

with pytest.raises(KeyError):
emmo.Atom["hasPart"]

emmo.Atom["altLabel"] = "Element"
setassert(emmo.Atom["altLabel"], {"ChemicalElement", "Element"})

del emmo.Atom["altLabel"]
assert emmo.Atom["altLabel"] == []

emmo.Atom.altLabel = "ChemicalElement"
assert emmo.Atom["altLabel"] == ["ChemicalElement"]

assert emmo.Atom.is_defined == False
assert emmo.Holistic.is_defined == True
assert (
emmo.wikipediaReference
) # Check that wikipediaReference is in ontology
assert (
emmo.Atom.wikipediaReference == []
) # Check that wikipediaReference can be acceses as attribute

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.MolecularEntity}

assert set(emmo.Atom.get_annotations().keys()) == {
"prefLabel",
"altLabel",
"elucidation",
"comment",
}


# Test item access/assignment/deletion for classes
assert set(emmo.Atom["altLabel"]) == {"ChemicalElement"}

with pytest.raises(KeyError):
emmo.Atom["hasPart"]

emmo.Atom["altLabel"] = "Element"
assert set(emmo.Atom["altLabel"]) == {"ChemicalElement", "Element"}

del emmo.Atom["altLabel"]
assert emmo.Atom["altLabel"] == []

emmo.Atom["altLabel"] = "ChemicalElement"
assert emmo.Atom["altLabel"] == ["ChemicalElement"]


assert emmo.Atom.is_defined == False
assert emmo.Holistic.is_defined == True

# TODO: Fix disjoint_with().
# It seems not to take into account disjoint unions.
Expand Down
7 changes: 7 additions & 0 deletions tests/ontopy_tests/test_prefix.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import TYPE_CHECKING
import pytest
from ontopy.utils import NoSuchLabelError
import warnings

if TYPE_CHECKING:
from pathlib import Path
Expand Down Expand Up @@ -37,6 +38,12 @@ def test_prefix(testonto: "Ontology", emmo: "Ontology") -> None:
"TestClass", prefix="models"
)

with pytest.raises(ValueError):
testonto.get_by_label_all(" ")

with pytest.raises(TypeError):
testonto.get_by_label(1)


def test_prefix_emmo(emmo: "Ontology") -> None:
"""Test prefix in ontology"""
Expand Down
2 changes: 1 addition & 1 deletion tests/ontopy_tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ontopy.utils as utils
from testutils import get_triples, has_triple
from utilities import get_triples, has_triple


def test_annotate_source(testonto: "Ontology"):
Expand Down
1 change: 0 additions & 1 deletion tests/test_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from ontopy import get_ontology


thisdir = Path(__file__).resolve().parent

onto = get_ontology(
Expand Down
Binary file modified tests/test_excelparser/onto.xlsx
100755 → 100644
Binary file not shown.
Binary file modified tests/test_excelparser/onto_only_classes.xlsx
100755 → 100644
Binary file not shown.
Binary file modified tests/test_excelparser/onto_update.xlsx
Binary file not shown.
Binary file modified tests/test_excelparser/onto_update_only_classes.xlsx
Binary file not shown.
Loading

0 comments on commit 24b86ad

Please sign in to comment.