Skip to content

Commit

Permalink
Adding tests for import behavior. Adding import_depth to OntologyReso…
Browse files Browse the repository at this point in the history
…urce and CLI, to determine how deep in import hierarchy to traverse (#249)

* Adding tests for import behavior.
Adding import_depth to OntologyResource and CLI,
to determine how deep in import hierarchy to traverse

* black

* Skipping pronto import test; awaiting althonos/pronto#186
  • Loading branch information
cmungall authored Aug 30, 2022
1 parent 1ac1ac4 commit 7e9e8b4
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 23 deletions.
16 changes: 14 additions & 2 deletions src/oaklib/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,11 +560,23 @@ def chain_it(v):
"--autosave/--no-autosave",
help="For commands that mutate the ontology, this determines if these are automatically saved in place",
)
@click.option(
"--import-depth",
type=click.INT,
help="Maximum depth in the import tree to traverse",
)
@input_option
@input_type_option
@add_option
def main(
verbose: int, quiet: bool, input: str, input_type: str, add: List, save_as: str, autosave: bool
verbose: int,
quiet: bool,
input: str,
input_type: str,
add: List,
save_as: str,
autosave: bool,
import_depth: Optional[int],
):
"""Run the oaklib Command Line.
Expand Down Expand Up @@ -593,7 +605,7 @@ def main(

if input:
impl_class: Type[OntologyInterface]
resource = get_resource_from_shorthand(input, format=input_type)
resource = get_resource_from_shorthand(input, format=input_type, import_depth=import_depth)
impl_class = resource.implementation_class
logging.info(f"RESOURCE={resource}")
settings.impl = impl_class(resource)
Expand Down
7 changes: 5 additions & 2 deletions src/oaklib/implementations/pronto/pronto_implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,15 @@ def __post_init__(self):
if self.wrapped_ontology is None:
resource = self.resource
logging.info(f"Pronto using resource: {resource}")
kwargs = {}
if resource and resource.import_depth is not None:
kwargs["import_depth"] = resource.import_depth
if resource is None:
ontology = Ontology()
elif resource.local:
ontology = Ontology(str(resource.local_path))
ontology = Ontology(str(resource.local_path), **kwargs)
else:
ontology = Ontology.from_obo_library(resource.slug)
ontology = Ontology.from_obo_library(resource.slug, **kwargs)
self.wrapped_ontology = ontology

@classmethod
Expand Down
4 changes: 1 addition & 3 deletions src/oaklib/interfaces/search_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
from oaklib.interfaces.basic_ontology_interface import BasicOntologyInterface
from oaklib.types import CURIE

__all__ = [
"SearchConfiguration",
]
__all__ = ["SearchConfiguration", "SearchInterface"]


class SearchInterface(BasicOntologyInterface, ABC):
Expand Down
42 changes: 31 additions & 11 deletions src/oaklib/resource.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING, Type, Union
from typing import TYPE_CHECKING, Optional, Type, Union

from class_resolver import HintOrType

Expand All @@ -21,16 +21,36 @@ class OntologyResource:
"""

slug: str = None
directory: str = None
scheme: str = None
format: str = None
url: str = None
readonly: bool = False
provider: str = None
local: bool = False
in_memory: bool = False
data: str = None
implementation_class: Union[Type] = None
"""Name or path of ontology resource"""

directory: Optional[str] = None
"""For local resources, the directory where the serialization is found"""

scheme: Optional[str] = None
"""The scheme of the resource, e.g pronto, sqlite"""

format: Optional[str] = None
"""For serialized resources, the serialization format"""

url: Optional[str] = None
"""For remote resources, the URL from which it can be obtained"""

readonly: Optional[bool] = False
"""Typically true for remote resources"""

provider: Optional[str] = None

local: Optional[bool] = True
"""Is the resource locally on disk or remote?"""

in_memory: Optional[bool] = False

data: Optional[str] = None

implementation_class: Optional[Union[Type]] = None

import_depth: Optional[int] = None
"""If set, this determines the maximum depth in the import tree to follow"""

@property
def local_path(self) -> Path:
Expand Down
8 changes: 6 additions & 2 deletions src/oaklib/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,15 @@ def get_resource_imp_class_from_suffix_descriptor(
return impl_class, resource


def get_resource_from_shorthand(descriptor: str, format: str = None) -> OntologyResource:
def get_resource_from_shorthand(
descriptor: str, format: str = None, import_depth: Optional[int] = None
) -> OntologyResource:
"""
Maps from a shorthand descriptor to an OntologyResource.
:param descriptor:
:param format:
:param format: file format/syntax, e.g obo, turtle
:param import_depth: maximum import depth to traverse
:return:
"""
from oaklib.implementations import (
Expand All @@ -137,6 +140,7 @@ def get_resource_from_shorthand(descriptor: str, format: str = None) -> Ontology
)

resource = OntologyResource(format=format)
resource.import_depth = import_depth
resource.slug = descriptor
impl_class: Optional[Type[OntologyInterface]] = None
if descriptor:
Expand Down
6 changes: 6 additions & 0 deletions tests/input/catalog-v001.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<catalog prefer="public" xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">

<!-- ./ -->
<uri id="User Entered Import Resolution" name="http://purl.obolibrary.org/obo/test_imported_ontology.owl" uri="./test_imported_ontology.owl"/>
</catalog>
7 changes: 7 additions & 0 deletions tests/input/test_import_root.obo
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
format-version: 1.2
ontology: test_import_root
import: http://purl.obolibrary.org/obo/test_imported_ontology.owl

[Term]
id: PATO:0000001
name: term in root
61 changes: 61 additions & 0 deletions tests/input/test_import_root.owl
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0"?>
<rdf:RDF xmlns="http://purl.obolibrary.org/obo/test_import_root.owl#"
xml:base="http://purl.obolibrary.org/obo/test_import_root.owl"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:oboInOwl="http://www.geneontology.org/formats/oboInOwl#">
<owl:Ontology rdf:about="http://purl.obolibrary.org/obo/test_import_root.owl">
<owl:imports rdf:resource="http://purl.obolibrary.org/obo/test_imported_ontology.owl"/>
</owl:Ontology>



<!--
///////////////////////////////////////////////////////////////////////////////////////
//
// Annotation properties
//
///////////////////////////////////////////////////////////////////////////////////////
-->




<!-- http://www.geneontology.org/formats/oboInOwl#id -->

<owl:AnnotationProperty rdf:about="http://www.geneontology.org/formats/oboInOwl#id"/>



<!-- http://www.w3.org/2000/01/rdf-schema#label -->

<owl:AnnotationProperty rdf:about="http://www.w3.org/2000/01/rdf-schema#label"/>



<!--
///////////////////////////////////////////////////////////////////////////////////////
//
// Classes
//
///////////////////////////////////////////////////////////////////////////////////////
-->




<!-- http://purl.obolibrary.org/obo/PATO_1 -->

<owl:Class rdf:about="http://purl.obolibrary.org/obo/PATO_1">
<oboInOwl:id rdf:datatype="http://www.w3.org/2001/XMLSchema#string">PATO:1</oboInOwl:id>
<rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">term in root</rdfs:label>
</owl:Class>
</rdf:RDF>



<!-- Generated by the OWL API (version 4.5.6) https://github.com/owlcs/owlapi -->

6 changes: 6 additions & 0 deletions tests/input/test_imported_ontology.obo
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
format-version: 1.2
ontology: test_imported_ontology

[Term]
id: PATO:2
name: term in imported
69 changes: 69 additions & 0 deletions tests/input/test_imported_ontology.owl
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?xml version="1.0"?>
<rdf:RDF xmlns="http://purl.obolibrary.org/obo/test_imported_ontology.owl#"
xml:base="http://purl.obolibrary.org/obo/test_imported_ontology.owl"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:oboInOwl="http://www.geneontology.org/formats/oboInOwl#">
<owl:Ontology rdf:about="http://purl.obolibrary.org/obo/test_imported_ontology.owl">
<oboInOwl:hasOBOFormatVersion rdf:datatype="http://www.w3.org/2001/XMLSchema#string">1.2</oboInOwl:hasOBOFormatVersion>
</owl:Ontology>



<!--
///////////////////////////////////////////////////////////////////////////////////////
//
// Annotation properties
//
///////////////////////////////////////////////////////////////////////////////////////
-->




<!-- http://www.geneontology.org/formats/oboInOwl#hasOBOFormatVersion -->

<owl:AnnotationProperty rdf:about="http://www.geneontology.org/formats/oboInOwl#hasOBOFormatVersion">
<rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">has_obo_format_version</rdfs:label>
</owl:AnnotationProperty>



<!-- http://www.geneontology.org/formats/oboInOwl#id -->

<owl:AnnotationProperty rdf:about="http://www.geneontology.org/formats/oboInOwl#id"/>



<!-- http://www.w3.org/2000/01/rdf-schema#label -->

<owl:AnnotationProperty rdf:about="http://www.w3.org/2000/01/rdf-schema#label"/>



<!--
///////////////////////////////////////////////////////////////////////////////////////
//
// Classes
//
///////////////////////////////////////////////////////////////////////////////////////
-->




<!-- http://purl.obolibrary.org/obo/PATO_2 -->

<owl:Class rdf:about="http://purl.obolibrary.org/obo/PATO_2">
<oboInOwl:id rdf:datatype="http://www.w3.org/2001/XMLSchema#string">PATO:2</oboInOwl:id>
<rdfs:label rdf:datatype="http://www.w3.org/2001/XMLSchema#string">term in imported</rdfs:label>
</owl:Class>
</rdf:RDF>



<!-- Generated by the OWL API (version 4.5.6) https://github.com/owlcs/owlapi -->

36 changes: 33 additions & 3 deletions tests/test_implementations/test_pronto.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import unittest

import pronto
from kgcl_schema.datamodel import kgcl

from oaklib.datamodels import obograph
Expand Down Expand Up @@ -103,7 +104,6 @@ def test_metadata(self):
def test_labels(self):
"""
Tests labels can be retrieved, and no label is retrieved when a term does not exist
:return:
"""
oi = self.oi
label = oi.label("GO:0005773")
Expand Down Expand Up @@ -143,8 +143,6 @@ def test_synonyms(self):
def test_mappings(self):
oi = self.oi
mappings = list(oi.get_sssom_mappings_by_curie(NUCLEUS))
# for m in mappings:
# logging.info(yaml_dumper.dumps(m))
assert any(m for m in mappings if m.object_id == "Wikipedia:Cell_nucleus")
self.assertEqual(len(mappings), 2)
for m in mappings:
Expand Down Expand Up @@ -177,6 +175,38 @@ def test_from_obo_library(self):
curies = oi.curies_by_label("shape")
self.assertEqual(["PATO:0000052"], curies)

@unittest.skip("https://github.com/althonos/pronto/issues/186")
def test_import_behavior(self):
"""
Tests behavior of owl:imports
by default, imports should be followed
See: https://github.com/INCATools/ontology-access-kit/issues/248
"""
for slug in ["test_import_root.obo", "test_import_root.obo"]:
resource = OntologyResource(slug=slug, directory=INPUT_DIR, local=True)
# print(resource.local_path)
# currently throws exception
pronto.Ontology(resource.local_path)
oi = ProntoImplementation.create(resource)
terms = list(oi.entities(owl_type="owl:Class"))
self.assertEqual(2, len(terms))

def test_no_import_depth(self):
"""
Tests behavior of owl:imports
do not follow imports if depth is set to zero
See: https://github.com/INCATools/ontology-access-kit/issues/248
"""
for slug in ["test_import_root.obo", "test_import_root.obo"]:
resource = OntologyResource(slug=slug, directory=INPUT_DIR, local=True, import_depth=0)
oi = ProntoImplementation(resource)
terms = list(oi.entities(owl_type="owl:Class"))
self.assertEqual(1, len(terms))

@unittest.skip("Hide warnings")
def test_from_owl(self):
r = OntologyResource(local=True, slug="go-nucleus.owl", directory=INPUT_DIR)
Expand Down
15 changes: 15 additions & 0 deletions tests/test_implementations/test_sparql.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
TEST_RDF = INPUT_DIR / "go-nucleus.owl.ttl"
TEST_INST_RDF = INPUT_DIR / "inst.owl.ttl"
TEST_MUTABLE_RDF = OUTPUT_DIR / "go-nucleus.owl.ttl"
TEST_IMPORTER = INPUT_DIR / "test_import_root.owl"


class TestSparqlImplementation(unittest.TestCase):
Expand Down Expand Up @@ -249,3 +250,17 @@ def test_mutable(self):
)
self.assertCountEqual([], list(oi.descendants(NUCLEUS, predicates=preds, reflexive=False)))
oi.save()

@unittest.skip("not yet implemented")
def test_import_behavior(self):
"""
Tests behavior of owl:imports
by default, imports should be followed
See: https://github.com/INCATools/ontology-access-kit/issues/248
"""
resource = OntologyResource(slug=str(TEST_IMPORTER))
oi = SparqlImplementation(resource)
terms = list(oi.entities(owl_type="owl:Class"))
self.assertCountEqual(["PATO:1", "PATO:2"], terms)

0 comments on commit 7e9e8b4

Please sign in to comment.