diff --git a/src/oaklib/cli.py b/src/oaklib/cli.py
index be9aa5615..2d1798ce0 100644
--- a/src/oaklib/cli.py
+++ b/src/oaklib/cli.py
@@ -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.
@@ -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)
diff --git a/src/oaklib/implementations/pronto/pronto_implementation.py b/src/oaklib/implementations/pronto/pronto_implementation.py
index a17e04fdd..d782932cd 100644
--- a/src/oaklib/implementations/pronto/pronto_implementation.py
+++ b/src/oaklib/implementations/pronto/pronto_implementation.py
@@ -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
diff --git a/src/oaklib/interfaces/search_interface.py b/src/oaklib/interfaces/search_interface.py
index 0ca9ca178..e01b39dbb 100644
--- a/src/oaklib/interfaces/search_interface.py
+++ b/src/oaklib/interfaces/search_interface.py
@@ -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):
diff --git a/src/oaklib/resource.py b/src/oaklib/resource.py
index 8488ac5f5..026b5eede 100644
--- a/src/oaklib/resource.py
+++ b/src/oaklib/resource.py
@@ -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
@@ -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:
diff --git a/src/oaklib/selector.py b/src/oaklib/selector.py
index 471483ebd..490b1dc6d 100644
--- a/src/oaklib/selector.py
+++ b/src/oaklib/selector.py
@@ -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 (
@@ -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:
diff --git a/tests/input/catalog-v001.xml b/tests/input/catalog-v001.xml
new file mode 100644
index 000000000..28309b74c
--- /dev/null
+++ b/tests/input/catalog-v001.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/tests/input/test_import_root.obo b/tests/input/test_import_root.obo
new file mode 100644
index 000000000..44ab15c7e
--- /dev/null
+++ b/tests/input/test_import_root.obo
@@ -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
diff --git a/tests/input/test_import_root.owl b/tests/input/test_import_root.owl
new file mode 100644
index 000000000..4e23d45a8
--- /dev/null
+++ b/tests/input/test_import_root.owl
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PATO:1
+ term in root
+
+
+
+
+
+
+
diff --git a/tests/input/test_imported_ontology.obo b/tests/input/test_imported_ontology.obo
new file mode 100644
index 000000000..ded0c3252
--- /dev/null
+++ b/tests/input/test_imported_ontology.obo
@@ -0,0 +1,6 @@
+format-version: 1.2
+ontology: test_imported_ontology
+
+[Term]
+id: PATO:2
+name: term in imported
diff --git a/tests/input/test_imported_ontology.owl b/tests/input/test_imported_ontology.owl
new file mode 100644
index 000000000..cac061856
--- /dev/null
+++ b/tests/input/test_imported_ontology.owl
@@ -0,0 +1,69 @@
+
+
+
+ 1.2
+
+
+
+
+
+
+
+
+
+
+
+
+ has_obo_format_version
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PATO:2
+ term in imported
+
+
+
+
+
+
+
diff --git a/tests/test_implementations/test_pronto.py b/tests/test_implementations/test_pronto.py
index 0f7f590fa..7a39d30ba 100644
--- a/tests/test_implementations/test_pronto.py
+++ b/tests/test_implementations/test_pronto.py
@@ -1,6 +1,7 @@
import logging
import unittest
+import pronto
from kgcl_schema.datamodel import kgcl
from oaklib.datamodels import obograph
@@ -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")
@@ -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:
@@ -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)
diff --git a/tests/test_implementations/test_sparql.py b/tests/test_implementations/test_sparql.py
index 2dd85379b..916220845 100644
--- a/tests/test_implementations/test_sparql.py
+++ b/tests/test_implementations/test_sparql.py
@@ -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):
@@ -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)