Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bypass punning in ontodoc. #532

Merged
merged 16 commits into from
Feb 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions ontopy/ontodoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,23 +392,32 @@ def itemdoc(
# Instances (individuals)
if hasattr(item, "instances"):
points = []
for entity in [
_ for _ in item.instances() if item in _.is_instance_of
]:
points.append(
point_style.format(
point=asstring(entity, link_style), ontology=onto

for instance in item.instances():
if isinstance(instance.is_instance_of, property):
warnings.warn(
f'Ignoring instance "{instance}" which is both and '
"indivudual and class. Ontodoc does not support "
"punning at the present moment."
)
)
if points:
value = points_style.format(
points="".join(points), ontology=onto
)
doc.append(
annotation_style.format(
key="Individuals", value=value, ontology=onto
continue
if item in instance.is_instance_of:
points.append(
point_style.format(
point=asstring(instance, link_style),
ontology=onto,
)
)
)
if points:
value = points_style.format(
points="".join(points), ontology=onto
)
doc.append(
annotation_style.format(
key="Individuals", value=value, ontology=onto
)
)

return "\n".join(doc)

def itemsdoc(self, items, header_level=3):
Expand Down
4 changes: 4 additions & 0 deletions ontopy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ class UnknownVersion(EMMOntoPyException):
"""Cannot retrieve version from a package."""


class IndividualWarning(EMMOntoPyWarning):
"""A warning related to an individual, e.g. punning."""


class NoSuchLabelError(LookupError, AttributeError, EMMOntoPyException):
"""Error raised when a label cannot be found."""

Expand Down
24 changes: 24 additions & 0 deletions tests/testonto/testonto_w_individual.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@prefix : <http://emmo.info/testonto#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@base <http://emmo.info/testonto> .

<http://emmo.info/testonto> rdf:type owl:Ontology ;
owl:versionIRI <http://emmo.info/testonto/0.1.0> ;
owl:versionInfo "0.1.0" .

skos:prefLabel rdf:type owl:AnnotationProperty .
skos:altLabel rdf:type owl:AnnotationProperty .

:testclass rdf:type owl:Class ;
rdfs:subClassOf owl:Thing ;
skos:prefLabel "TestClass"@en .


:testindividual rdf:type owl:NamedIndividual ,
:testclass ;
skos:prefLabel "testindividual"@en .
23 changes: 23 additions & 0 deletions tests/testonto/testonto_w_punning.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@prefix : <http://emmo.info/testonto#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@base <http://emmo.info/testonto> .

<http://emmo.info/testonto> rdf:type owl:Ontology ;
owl:versionIRI <http://emmo.info/testonto/0.1.0> ;
owl:versionInfo "0.1.0" .

skos:prefLabel rdf:type owl:AnnotationProperty .
skos:altLabel rdf:type owl:AnnotationProperty .

:testclass rdf:type owl:Class ;
rdfs:subClassOf owl:Thing ;
skos:prefLabel "TestClass"@en .


:testclass rdf:type owl:NamedIndividual ,
:testclass .
123 changes: 100 additions & 23 deletions tests/tools/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,110 @@

if TYPE_CHECKING:
from types import ModuleType
from typing import Any, Dict
from typing import Callable


@pytest.fixture
def tool(request: "Dict[str, Any]") -> "ModuleType":
"""Import a tool as a module."""
@pytest.fixture(scope="module", autouse=True)
def rename_tools() -> None:
"""Add a `.py` extension to all tools.

Run prior to all tests in this module.
First, rename all tools (adding a `.py` suffix) to make them importable as a
module. Then stop executing this fixture for a while (yield) and run all the tests
in the module. Then after they're done, rename the tools back (remove the `.py`
suffix) and also return `sys.path` to its original state prior to running the
tests.
To make the importability work, the `tools` folder had to be added to the
`sys.path`.
"""
from copy import deepcopy
import importlib
import os
from pathlib import Path
import shutil
import sys

original_sys_path = deepcopy(sys.path)
original_tool_path: Path = (
Path(__file__).resolve().parent.parent.parent / "tools" / request.param
)
if str(original_tool_path.parent) not in sys.path:
sys.path.append(str(original_tool_path.parent))

assert (
original_tool_path.exists()
), f"The requested tool ({request.param}) was not found in {original_tool_path.parent}"
try:
tool_path = original_tool_path.rename(
original_tool_path.with_name(f"{request.param}.py")
)
yield importlib.import_module(request.param)
finally:
if tool_path and tool_path.exists():
tool_path.rename(tool_path.with_name(request.param))
sys.path = original_sys_path
tools_path: Path = Path(__file__).resolve().parent.parent.parent / "tools"

if str(tools_path) not in sys.path:
sys.path.append(str(tools_path))

# Add ".py" suffix to all tools
for (
dirpath,
dirnames,
filenames,
) in os.walk(tools_path):
if dirpath != str(tools_path):
continue

if dirnames:
for dirname in dirnames:
if dirname == "__pycache__":
shutil.rmtree(
Path(dirpath) / "__pycache__", ignore_errors=True
)

for filename in filenames:
filepath = Path(dirpath) / filename
assert (
filepath.suffix == ""
), f"A suffix was found (not expected) for file: {filepath}"

filepath.rename(filepath.with_suffix(".py"))

yield

# Remove ".py" suffix from all tools
for (
dirpath,
dirnames,
filenames,
) in os.walk(tools_path):
if dirpath != str(tools_path):
continue

if dirnames:
for dirname in dirnames:
if dirname == "__pycache__":
shutil.rmtree(
Path(dirpath) / "__pycache__", ignore_errors=True
)

for filename in filenames:
filepath = Path(dirpath) / filename
assert (
filepath.suffix == ".py"
), f"A suffix was NOT found (not expected) for file: {filepath}"

filepath.rename(filepath.with_suffix(""))

sys.path = original_sys_path


@pytest.fixture
def get_tool() -> "Callable[[str], ModuleType]":
"""Import a tool as a module.

Requires the fixture `rename_tools` to have been run already.
"""
import importlib
from pathlib import Path
import sys

def _get_tool(name: str) -> "ModuleType":
"""Import and return named tool."""
tool_path: Path = (
Path(__file__).resolve().parent.parent.parent / "tools" / name
).with_suffix(".py")
assert (
str(tool_path.parent) in sys.path
), f"'tools' dir not found in sys.path. Did `rename_tools` fixture run?\nsys.path: {sys.path}"

assert (
tool_path.exists()
), f"The requested tool ({name}) was not found in {tool_path.parent}."

return importlib.import_module(name)

return _get_tool
20 changes: 15 additions & 5 deletions tests/tools/test_emmocheck.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
"""Test the `emmocheck` tool."""
import pytest
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from types import ModuleType
from typing import Callable

@pytest.mark.parametrize("tool", ["emmocheck"], indirect=True)
def test_run(tool) -> None:
"""Check that running `emmocheck` works."""

def test_run(get_tool: "Callable[[str], ModuleType]") -> None:
"""Check that running `emmocheck` works.

Parameters:
get_tool: Local module fixture to load a named tool as a module.
See the current folder's `conftest.py` file.

"""
from pathlib import Path

test_file = (
Path(__file__).resolve().parent.parent / "testonto" / "models.ttl"
)
emmocheck = get_tool("emmocheck")

tool.main([str(test_file)])
emmocheck.main([str(test_file)])
33 changes: 24 additions & 9 deletions tests/tools/test_excel2onto.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
"""Test the `ontograph` tool."""
from pathlib import Path
import os
import pytest
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path
from types import ModuleType
from typing import Callable


def test_run(get_tool: "Callable[[str], ModuleType]", tmpdir: "Path") -> None:
"""Check that running `excel2onto` works.

Parameters:
get_tool: Local module fixture to load a named tool as a module.
See the current folder's `conftest.py` file.
tmpdir: A generic pytest fixture to generate a temporary directory, which will
exist only for the lifetime of this test function.

"""
from pathlib import Path

@pytest.mark.parametrize("tool", ["excel2onto"], indirect=True)
def test_run(tool, tmpdir: Path) -> None:
"""Check that running `excel2onto` works."""
test_file = (
Path(__file__).resolve().parent.parent
/ "test_excelparser"
Expand All @@ -17,10 +29,13 @@ def test_run(tool, tmpdir: Path) -> None:
/ "test_excelparser"
/ "onto_update.xlsx"
)
excel2onto = get_tool("excel2onto")

tool.main([f"--output={str(tmpdir)}/onto.ttl", "--force", str(test_file)])
excel2onto.main(
[f"--output={str(tmpdir)}/onto.ttl", "--force", str(test_file)]
)

tool.main(
excel2onto.main(
[
f"--output={str(tmpdir)}/onto.ttl",
"--force",
Expand All @@ -29,7 +44,7 @@ def test_run(tool, tmpdir: Path) -> None:
]
)

tool.main(
excel2onto.main(
[
f"--output={str(tmpdir)}/ontology.ttl",
"--force",
Expand Down
25 changes: 19 additions & 6 deletions tests/tools/test_ontoconvert.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
"""Test the `ontoconvert` tool."""
from pathlib import Path
from typing import TYPE_CHECKING

import pytest
if TYPE_CHECKING:
from pathlib import Path
from types import ModuleType
from typing import Callable


@pytest.mark.parametrize("tool", ["ontoconvert"], indirect=True)
def test_run(tool, tmpdir: Path) -> None:
"""Check that running `ontoconvert` works."""
def test_run(get_tool: "Callable[[str], ModuleType]", tmpdir: "Path") -> None:
"""Check that running `ontoconvert` works.

Parameters:
get_tool: Local module fixture to load a named tool as a module.
See the current folder's `conftest.py` file.
tmpdir: A generic pytest fixture to generate a temporary directory, which will
exist only for the lifetime of this test function.

"""
from pathlib import Path

test_file = (
Path(__file__).resolve().parent.parent / "testonto" / "models.ttl"
)
ontoconvert = get_tool("ontoconvert")

tool.main([str(test_file), str(tmpdir / "test.ttl")])
ontoconvert.main([str(test_file), str(tmpdir / "test.ttl")])
Loading