From e65ed6894935f7690eb2d07302d6d1213901bbd7 Mon Sep 17 00:00:00 2001 From: francescalb Date: Thu, 23 Nov 2023 18:12:16 +0100 Subject: [PATCH 1/9] Get_version updated to remove datatype --- ontopy/ontology.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ontopy/ontology.py b/ontopy/ontology.py index 2fa5fff27..fde54d2aa 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -1555,7 +1555,7 @@ def get_version(self, as_iri=False) -> str: "No versionIRI or versionInfo " f"in Ontology {self.base_iri!r}" ) _, _, version_info = tokens[0] - return version_info.strip('"').strip("'") + return version_info.split("^^")[0].strip('"') def set_version(self, version=None, version_iri=None): """Assign version to ontology by asigning owl:versionIRI. From 12ff93af0d588e4f959a5e04b1ea4a1ccbfdef23 Mon Sep 17 00:00:00 2001 From: francescalb Date: Thu, 23 Nov 2023 18:15:28 +0100 Subject: [PATCH 2/9] Updated req of owlready2 to <0.46 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 275fd0ba3..ff568cf07 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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.44 +Owlready2>=0.28,!=0.32,!=0.34,<0.46 packaging>=21.0,<24 pandas>=1.2,<2.2 Pygments>=2.7.4,<3 From 37a0449d388b7b681f33de3934dae75853ce44aa Mon Sep 17 00:00:00 2001 From: francescalb Date: Thu, 23 Nov 2023 18:56:40 +0100 Subject: [PATCH 3/9] Added a possibilty to copy the ontology This is done by saving it in a temporary file and loading it as new. --- ontopy/ontology.py | 12 ++++++++++++ tests/ontopy_tests/test_copy.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/ontopy_tests/test_copy.py diff --git a/ontopy/ontology.py b/ontopy/ontology.py index fde54d2aa..38a5dfffc 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -1939,6 +1939,18 @@ def new_annotation_property( """ return self.new_entity(name, parent, "annotation_property") + def copy(self): + """Return a copy of the ontology.""" + with tempfile.TemporaryDirectory() as handle: + tmpfile = os.path.join(handle, "tmp.owl") + + self.save( + tmpfile, + squash=True, + ) + ontology = get_ontology(tmpfile).load() + return ontology + class BlankNode: """Represents a blank node. diff --git a/tests/ontopy_tests/test_copy.py b/tests/ontopy_tests/test_copy.py new file mode 100644 index 000000000..3502223c7 --- /dev/null +++ b/tests/ontopy_tests/test_copy.py @@ -0,0 +1,33 @@ +from typing import TYPE_CHECKING +import pytest + +if TYPE_CHECKING: + from pathlib import Path + + +def test_copy(testonto: "Ontology", repo_dir: "Path") -> None: + """Test adding entities to ontology""" + + from ontopy import get_ontology + from ontopy.utils import NoSuchLabelError + + # create new reference to ontology + testonto = get_ontology( + repo_dir / "tests" / "testonto" / "testonto.ttl" + ).load() + testonto_ref = testonto + + # add new entity to ontology + testonto.new_entity("FantasyClass", testonto.TestClass) + + assert testonto_ref.FantasyClass == testonto.FantasyClass + + # make a copy and check that new entity in original is not in copy + testonto_copy = testonto.copy() + + testonto.new_entity("FantasyClass2", testonto_copy.TestClass) + + assert testonto.FantasyClass2 + + with pytest.raises(NoSuchLabelError): + assert testonto_copy.FantasyClass2 From dc4a7e93cc817315b428234588adbe8801afa7fe Mon Sep 17 00:00:00 2001 From: francescalb Date: Fri, 24 Nov 2023 15:50:07 +0100 Subject: [PATCH 4/9] Added test for copy_function --- tests/ontopy_tests/test_copy.py | 81 +++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/tests/ontopy_tests/test_copy.py b/tests/ontopy_tests/test_copy.py index 3502223c7..5ceb0c8eb 100644 --- a/tests/ontopy_tests/test_copy.py +++ b/tests/ontopy_tests/test_copy.py @@ -5,24 +5,19 @@ from pathlib import Path -def test_copy(testonto: "Ontology", repo_dir: "Path") -> None: - """Test adding entities to ontology""" +def test_copy(testonto: "Ontology") -> None: + """Test copying an ontology""" from ontopy import get_ontology from ontopy.utils import NoSuchLabelError - # create new reference to ontology - testonto = get_ontology( - repo_dir / "tests" / "testonto" / "testonto.ttl" - ).load() testonto_ref = testonto - # add new entity to ontology + # Add new entity to ontology testonto.new_entity("FantasyClass", testonto.TestClass) - assert testonto_ref.FantasyClass == testonto.FantasyClass - # make a copy and check that new entity in original is not in copy + # Mgake a copy and check that new entity in original is not in copy testonto_copy = testonto.copy() testonto.new_entity("FantasyClass2", testonto_copy.TestClass) @@ -31,3 +26,71 @@ def test_copy(testonto: "Ontology", repo_dir: "Path") -> None: with pytest.raises(NoSuchLabelError): assert testonto_copy.FantasyClass2 + + +def test_copy_with_save(testonto: "Ontology", tmp_path: "Path") -> None: + """Test saving an ontology and then copying it. + When saving an ontology without keeping the python name triples + it is actually copied before saving. + + This test checks that the local ontology is not changed when + saving and that saving is done with or without python name triples + as requested. + """ + + from ontopy import get_ontology + from ontopy.utils import NoSuchLabelError + + def python_name_triples(ontology): + return ontology.get_triples( + s=None, + p=ontology._abbreviate( + "http://www.lesfleursdunormal.fr/static/_downloads/" + "owlready_ontology.owl#python_name" + ), + o=None, + ) + + assert len(list(python_name_triples(testonto))) == 0 + + # owlready2 adds python_name triples for properties + testonto.sync_python_names() + + assert len(list(python_name_triples(testonto))) == 3 + + # save, not keeping python name triples in the saved ontology + testonto.save(tmp_path / "testonto_1.ttl", keep_python_names=False) + + # load the saved ontology and check that python name triples are not there + testonto1 = get_ontology(tmp_path / "testonto_1.ttl").load() + + assert len(list(python_name_triples(testonto1))) == 0 + + # check that the python name triples are still in the ontology + + assert len(list(python_name_triples(testonto))) == 3 + + # save, keeping python name triples in the saved ontology + testonto.save(tmp_path / "testonto_2.ttl", keep_python_names=True) + + # load the saved ontology and check that python name triples are there + testonto2 = get_ontology(tmp_path / "testonto_2.ttl").load() + + assert len(list(python_name_triples(testonto2))) == 3 + + assert testonto2 == testonto + + +def test_copy_emmo(emmo: "Ontology") -> None: + """Test copying the emmo ontology""" + + from emmopy import get_emmo + + # Make a copy and check that new entity in original is not in copy + new_emmo = emmo.copy() + + assert new_emmo == emmo + + emmo_from_web = get_emmo() + + assert new_emmo == emmo_from_web From 2612ee0974f4ab6f084f96b07de00387648795fa Mon Sep 17 00:00:00 2001 From: francescalb Date: Fri, 24 Nov 2023 16:19:49 +0100 Subject: [PATCH 5/9] Added copy version 1 in ontology Note that this is done by squashing and saving which also changes the original ontology. This should be avoided. The excelparser-ontology fails for this reason? --- ontopy/ontology.py | 89 +++++++++++++++++++++- tests/test_excelparser/test_excelparser.py | 11 +++ 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/ontopy/ontology.py b/ontopy/ontology.py index 38a5dfffc..ef504050e 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -851,6 +851,77 @@ def save( write_catalog_file=False, append_catalog=False, catalog_file="catalog-v001.xml", + keep_python_names=False, + ): + """Writes the ontology to file. + + Parameters + ---------- + filename: None | str | Path + Name of file to write to. If None, it defaults to the name + of the ontology with `format` as file extension. + format: str + Output format. The default is to infer it from `filename`. + dir: str | Path + If `filename` is a relative path, it is a relative path to `dir`. + mkdir: bool + Whether to create output directory if it does not exists. + owerwrite: bool + If true and `filename` exists, remove the existing file before + saving. The default is to append to an existing ontology. + recursive: bool + Whether to save imported ontologies recursively. This is + commonly combined with `filename=None`, `dir` and `mkdir`. + squash: bool + assert testonto_copy.FantasyClass2 + If true, rdflib will be used to save the current ontology + together with all its sub-ontologies into `filename`. + It make no sense to combine this with `recursive`. + write_catalog_file: bool + Whether to also write a catalog file to disk. + append_catalog: bool + Whether to append to an existing catalog file. + catalog_file: str | Path + Name of catalog file. If not an absolute path, it is prepended + to `dir`. + keep_python_names: bool + Whether to keep python names in the ontology. + """ + if keep_python_names: + newonto = self + else: + newonto = self.copy() + newonto._del_data_triple_spod( + p=newonto._abbreviate( + "http://www.lesfleursdunormal.fr/static/_downloads/" + "owlready_ontology.owl#python_name" + ) + ) + newonto._save( + filename=filename, + format=format, + dir=dir, + mkdir=mkdir, + overwrite=overwrite, + recursive=recursive, + squash=squash, + write_catalog_file=write_catalog_file, + append_catalog=append_catalog, + catalog_file=catalog_file, + ) + + def _save( + self, + filename=None, + format=None, + dir=".", + mkdir=False, + overwrite=False, + recursive=False, + squash=False, + write_catalog_file=False, + append_catalog=False, + catalog_file="catalog-v001.xml", ): """Writes the ontology to file. @@ -938,7 +1009,7 @@ def save( for onto in self.imported_ontologies: obase = onto.base_iri.rstrip("#/") newdir = Path(dir) / os.path.relpath(obase, base) - onto.save( + onto._save( filename=None, format=format, dir=newdir.resolve(), @@ -1944,11 +2015,25 @@ def copy(self): with tempfile.TemporaryDirectory() as handle: tmpfile = os.path.join(handle, "tmp.owl") - self.save( + self._save( tmpfile, + dir=handle, + # recursive=True, + write_catalog_file=True, + mkdir=True, + # overwrite=True, + # tmpfile, squash=True, ) + # print(self.TestClass) + # print out content of all files in tmpdir + for file in os.listdir(handle): + with open(os.path.join(handle, file), "r") as f: + print(f"File: {file}") + print(f.read()) + ontology = get_ontology(tmpfile).load() + # print(ontology.TestClass) return ontology diff --git a/tests/test_excelparser/test_excelparser.py b/tests/test_excelparser/test_excelparser.py index 0131b591d..a2fe4880b 100644 --- a/tests/test_excelparser/test_excelparser.py +++ b/tests/test_excelparser/test_excelparser.py @@ -33,6 +33,17 @@ def test_excelparser(repo_dir: "Path") -> None: ) ontology, catalog, errors = create_ontology_from_excel(xlspath, force=True) # ontology.save("test.ttl") # used for printing new ontology when debugging + # remove python_names added by owlready2 before comparing the ontologies. + # The emmontopy.ontology.save removes all triples with this relation as default. + ontology.save("test.ttl") # used for printing new ontology when debugging + + # onto2 = ontology.copy() + ontology._del_data_triple_spod( + p=ontology._abbreviate( + "http://www.lesfleursdunormal.fr/static/_downloads/" + "owlready_ontology.owl#python_name" + ) + ) assert onto == ontology assert errors.keys() == { "already_defined", From ecfd73b72b49b7dc5b88c52ae7877eae662a5628 Mon Sep 17 00:00:00 2001 From: francescalb Date: Mon, 27 Nov 2023 22:22:53 +0100 Subject: [PATCH 6/9] pre-commit updates Format is used as argument for ontology.save in owlready2 and cannot be changed. ontology.save in owlready2 takes file as argument, ontopy is updated accordingly and keeping filename as optional with deprecation warning. Same is done for dir to directory as dir is a predefined built-in in python and should not be redefined. --- ontopy/ontology.py | 95 +++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 39 deletions(-) diff --git a/ontopy/ontology.py b/ontopy/ontology.py index ef504050e..41580ab7d 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -841,9 +841,9 @@ def getmtime(path): def save( self, - filename=None, - format=None, - dir=".", + file=None, + format=None, # pylint: disable=redefined-builtin + directory=None, mkdir=False, overwrite=False, recursive=False, @@ -852,30 +852,32 @@ def save( append_catalog=False, catalog_file="catalog-v001.xml", keep_python_names=False, - ): + filename=None, # deprecated + dir=".", # pylint: disable=redefined-builtin # deprecated + ): # pylint: disable=too-many-arguments """Writes the ontology to file. Parameters ---------- - filename: None | str | Path + file: None | str | Path Name of file to write to. If None, it defaults to the name of the ontology with `format` as file extension. format: str - Output format. The default is to infer it from `filename`. - dir: str | Path - If `filename` is a relative path, it is a relative path to `dir`. + Output format. The default is to infer it from `file`. + directory: str | Path + If `file` is a relative path, it is a relative path to `directory`. mkdir: bool Whether to create output directory if it does not exists. owerwrite: bool - If true and `filename` exists, remove the existing file before + If true and `file` exists, remove the existing file before saving. The default is to append to an existing ontology. recursive: bool Whether to save imported ontologies recursively. This is - commonly combined with `filename=None`, `dir` and `mkdir`. + commonly combined with `file=None`, `directory` and `mkdir`. squash: bool assert testonto_copy.FantasyClass2 If true, rdflib will be used to save the current ontology - together with all its sub-ontologies into `filename`. + together with all its sub-ontologies into `file`. It make no sense to combine this with `recursive`. write_catalog_file: bool Whether to also write a catalog file to disk. @@ -883,10 +885,25 @@ def save( Whether to append to an existing catalog file. catalog_file: str | Path Name of catalog file. If not an absolute path, it is prepended - to `dir`. + to `directory`. keep_python_names: bool Whether to keep python names in the ontology. """ + if filename: + warnings.warn( + "The `filename` argument is deprecated. Use `file` instead.", + DeprecationWarning, + stacklevel=2, + ) + file = filename + if dir: + warnings.warn( + "The `dir` argument is deprecated. Use `directory` instead.", + DeprecationWarning, + stacklevel=2, + ) + directory = dir + if keep_python_names: newonto = self else: @@ -898,9 +915,9 @@ def save( ) ) newonto._save( - filename=filename, + file=file, format=format, - dir=dir, + directory=directory, mkdir=mkdir, overwrite=overwrite, recursive=recursive, @@ -912,9 +929,9 @@ def save( def _save( self, - filename=None, + file=None, format=None, - dir=".", + directory=".", mkdir=False, overwrite=False, recursive=False, @@ -927,24 +944,24 @@ def _save( Parameters ---------- - filename: None | str | Path + file: None | str | Path Name of file to write to. If None, it defaults to the name of the ontology with `format` as file extension. format: str - Output format. The default is to infer it from `filename`. - dir: str | Path - If `filename` is a relative path, it is a relative path to `dir`. + Output format. The default is to infer it from `file`. + directory: str | Path + If `file` is a relative path, it is a relative path to `directory` mkdir: bool Whether to create output directory if it does not exists. owerwrite: bool - If true and `filename` exists, remove the existing file before + If true and `file` exists, remove the existing file before saving. The default is to append to an existing ontology. recursive: bool Whether to save imported ontologies recursively. This is - commonly combined with `filename=None`, `dir` and `mkdir`. + commonly combined with `file=None`, `directory` and `mkdir`. squash: bool If true, rdflib will be used to save the current ontology - together with all its sub-ontologies into `filename`. + together with all its sub-ontologies into `file`. It make no sense to combine this with `recursive`. write_catalog_file: bool Whether to also write a catalog file to disk. @@ -952,7 +969,7 @@ def _save( Whether to append to an existing catalog file. catalog_file: str | Path Name of catalog file. If not an absolute path, it is prepended - to `dir`. + to `directory`. """ # pylint: disable=redefined-builtin,too-many-arguments # pylint: disable=too-many-statements,too-many-branches @@ -975,26 +992,26 @@ def _save( ) revmap = {value: key for key, value in FMAP.items()} - if filename is None: + if file is None: if format: fmt = revmap.get(format, format) - filename = f"{self.name}.{fmt}" + file = f"{self.name}.{fmt}" else: raise TypeError("`filename` and `format` cannot both be None.") - filename = os.path.join(dir, filename) - dir = Path(filename).resolve().parent + file = os.path.join(directory, file) + directory = Path(file).resolve().parent if mkdir: - outdir = Path(filename).parent.resolve() + outdir = Path(file).parent.resolve() if not outdir.exists(): outdir.mkdir(parents=True) if not format: - format = guess_format(filename, fmap=FMAP) + format = guess_format(file, fmap=FMAP) fmt = revmap.get(format, format) - if overwrite and filename and os.path.exists(filename): - os.remove(filename) + if overwrite and file and os.path.exists(file): + os.remove(file) EMMO = rdflib.Namespace( # pylint:disable=invalid-name "http://emmo.info/emmo#" @@ -1010,9 +1027,9 @@ def _save( obase = onto.base_iri.rstrip("#/") newdir = Path(dir) / os.path.relpath(obase, base) onto._save( - filename=None, + file=None, format=format, - dir=newdir.resolve(), + directory=newdir.resolve(), mkdir=mkdir, overwrite=overwrite, recursive=recursive, @@ -1038,9 +1055,9 @@ def _save( for triple in imports: graph.remove(triple) - graph.serialize(destination=filename, format=format) + graph.serialize(destination=file, format=format) elif format in OWLREADY2_FORMATS: - super().save(file=filename, format=fmt) + super().save(file=file, format=fmt) else: # The try-finally clause is needed for cleanup and because # we have to provide delete=False to NamedTemporaryFile @@ -1054,7 +1071,7 @@ def _save( super().save(tmpfile, format="rdfxml") graph = rdflib.Graph() graph.parse(tmpfile, format="xml") - graph.serialize(destination=filename, format=format) + graph.serialize(destination=file, format=format) finally: os.remove(tmpfile) @@ -1076,7 +1093,7 @@ def append(onto): write_catalog( mappings, output=catalog_file, - directory=dir, + directory=directory, append=append_catalog, ) @@ -2017,7 +2034,7 @@ def copy(self): self._save( tmpfile, - dir=handle, + directory=handle, # recursive=True, write_catalog_file=True, mkdir=True, From a2cae977e27f2147ee7178cec624e39921d092ea Mon Sep 17 00:00:00 2001 From: francescalb Date: Mon, 27 Nov 2023 22:36:46 +0100 Subject: [PATCH 7/9] Update argument name for save --- tools/ontoconvert | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ontoconvert b/tools/ontoconvert index b184b5b23..6216d9ce7 100755 --- a/tools/ontoconvert +++ b/tools/ontoconvert @@ -206,7 +206,7 @@ def main(argv: list = None): onto.save( args.output, format=output_format, - dir=args.output_dir, + directory=args.output_dir, mkdir=True, overwrite=args.overwrite, recursive=args.recursive, From 2a63ce411c16be9f3013ff5d971d17d1c572ad73 Mon Sep 17 00:00:00 2001 From: francescalb Date: Thu, 30 Nov 2023 14:20:00 +0100 Subject: [PATCH 8/9] Tryoing out different versions of copy --- ontopy/ontology.py | 57 +++++++++++++++++++++++++-------- tests/ontopy_tests/test_copy.py | 14 ++++++-- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/ontopy/ontology.py b/ontopy/ontology.py index 41580ab7d..09aeb4ac4 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -1025,7 +1025,7 @@ def _save( base = self.base_iri.rstrip("#/") for onto in self.imported_ontologies: obase = onto.base_iri.rstrip("#/") - newdir = Path(dir) / os.path.relpath(obase, base) + newdir = Path(directory) / os.path.relpath(obase, base) onto._save( file=None, format=format, @@ -1081,9 +1081,9 @@ def _save( def append(onto): obase = onto.base_iri.rstrip("#/") - newdir = Path(dir) / os.path.relpath(obase, base) + newdir = Path(directory) / os.path.relpath(obase, base) newpath = newdir.resolve() / f"{onto.name}.{fmt}" - relpath = os.path.relpath(newpath, dir) + relpath = os.path.relpath(newpath, directory) mappings[onto.get_version(as_iri=True)] = str(relpath) for imported in onto.imported_ontologies: append(imported) @@ -2029,30 +2029,61 @@ def new_annotation_property( def copy(self): """Return a copy of the ontology.""" - with tempfile.TemporaryDirectory() as handle: - tmpfile = os.path.join(handle, "tmp.owl") - + with tempfile.TemporaryDirectory() as dirname: + filename = os.path.join(dirname, "tmp.owl") self._save( - tmpfile, - directory=handle, - # recursive=True, + filename, + directory=dirname, + format="rdfxml", + recursive=True, write_catalog_file=True, mkdir=True, # overwrite=True, # tmpfile, - squash=True, + # squash=True, ) # print(self.TestClass) # print out content of all files in tmpdir - for file in os.listdir(handle): - with open(os.path.join(handle, file), "r") as f: + for file in os.listdir(dirname): + with open(os.path.join(dirname, file), "r") as f: print(f"File: {file}") print(f.read()) - ontology = get_ontology(tmpfile).load() + ontology = get_ontology(filename).load() # print(ontology.TestClass) return ontology + def copy2(self, recursive=True, copy_world=True): + """Return a copy of self. + + Arguments: + recursive: Whether to copy imported ontologies recursively. + copy_world: Whether to also copy the world. + """ + + if copy_world: + new_world = World() + self.world.get_triples() + # raise NotImplementedError( + # "Argument `copy_world` is not yet implemented." + # ) + + onto = Ontology( + world=World() if copy_world else self.world, + base_iri=self.base_iri, + name=self.name, + ) + onto.label_annotations = self.label_annotations[:] + onto.prefix = self.prefix + + if recursive: + for imported in self.imported_ontologies: + onto.imported_ontologies.append(imported.copy()) + else: + onto.imported_ontologies = self.imported_ontologies[:] + + return onto + class BlankNode: """Represents a blank node. diff --git a/tests/ontopy_tests/test_copy.py b/tests/ontopy_tests/test_copy.py index 5ceb0c8eb..0f041e3bf 100644 --- a/tests/ontopy_tests/test_copy.py +++ b/tests/ontopy_tests/test_copy.py @@ -17,13 +17,21 @@ def test_copy(testonto: "Ontology") -> None: testonto.new_entity("FantasyClass", testonto.TestClass) assert testonto_ref.FantasyClass == testonto.FantasyClass - # Mgake a copy and check that new entity in original is not in copy + # Make a copy and check that new entity in original is not in copy testonto_copy = testonto.copy() - testonto.new_entity("FantasyClass2", testonto_copy.TestClass) + testonto.new_entity("FantasyClass2", testonto.TestClass) assert testonto.FantasyClass2 - + print("testonto", list(testonto.classes())) + print(list(testonto_copy.classes())) + print("copied label annotations", testonto_copy.label_annotations) + print("copied prefix", testonto_copy.prefix) + assert testonto_copy.FantasyClass + print("*************************************") + print(set(testonto.classes()).difference(testonto_copy.classes())) + + print("*************************") with pytest.raises(NoSuchLabelError): assert testonto_copy.FantasyClass2 From 811e230003e21d7af77aab6a64ee1077d164c380 Mon Sep 17 00:00:00 2001 From: francescalb Date: Thu, 25 Jan 2024 11:49:10 +0100 Subject: [PATCH 9/9] Temporary commit --- ontopy/excelparser.py | 21 +++- ontopy/ontology.py | 44 ++++---- tests/ontopy_tests/test_copy.py | 11 +- tests/test_excelparser/onto.xlsx | Bin 24136 -> 24121 bytes tests/test_excelparser/test_excelparser.py | 24 ++-- tests/test_save.py | 123 ++++++++++++++++++--- 6 files changed, 167 insertions(+), 56 deletions(-) diff --git a/ontopy/excelparser.py b/ontopy/excelparser.py index eff2f5657..492f598a7 100755 --- a/ontopy/excelparser.py +++ b/ontopy/excelparser.py @@ -550,15 +550,24 @@ def get_metadata_from_dataframe( # pylint: disable=too-many-locals,too-many-bra # Add versionInfo try: - _add_literal( - metadata, - onto.metadata.versionInfo, - "Ontology version Info", - metadata=True, - only_one=True, + name_list = _parse_literal( + metadata, "Ontology version Info", metadata=True ) + # _add_literal( + # metadata, + onto.metadata.versionInfo.append(name_list[0]) + # "Ontology version Info", + # metadata=True, + # only_one=True, + # ) except AttributeError: pass + + print("----------------") + print(onto.get_version()) + onto.set_version(onto.get_version()) + print(onto.get_version(as_iri=True)) + print("----------------------") return onto, catalog diff --git a/ontopy/ontology.py b/ontopy/ontology.py index 7ae10521e..55a4d6ad4 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -873,7 +873,7 @@ def save( catalog_file="catalog-v001.xml", keep_python_names=False, filename=None, # deprecated - dir=".", # pylint: disable=redefined-builtin # deprecated + dir=None, # pylint: disable=redefined-builtin # deprecated ): # pylint: disable=too-many-arguments """Writes the ontology to file. @@ -909,6 +909,7 @@ def save( keep_python_names: bool Whether to keep python names in the ontology. """ + if filename: warnings.warn( "The `filename` argument is deprecated. Use `file` instead.", @@ -923,7 +924,8 @@ def save( stacklevel=2, ) directory = dir - + if not directory: + directory = "." if keep_python_names: newonto = self else: @@ -934,6 +936,7 @@ def save( "owlready_ontology.owl#python_name" ) ) + newonto._save( file=file, format=format, @@ -947,7 +950,7 @@ def save( catalog_file=catalog_file, ) - def _save( + def _save( # pylint: disable=too-many-branches self, file=None, format=None, @@ -1020,7 +1023,6 @@ def _save( raise TypeError("`filename` and `format` cannot both be None.") file = os.path.join(directory, file) directory = Path(file).resolve().parent - if mkdir: outdir = Path(file).parent.resolve() if not outdir.exists(): @@ -1039,7 +1041,6 @@ def _save( "`recursive` and `squash` should not both be true" ) layout = directory_layout(self) - for onto, path in layout.items(): fname = Path(directory) / f"{path}.{fmt}" onto._save( @@ -1059,14 +1060,14 @@ def _save( for onto, path in layout.items(): irimap[ onto.get_version(as_iri=True) - ] = f"{dir}/{path}.{fmt}" + ] = f"{directory}/{path}.{fmt}" catalog_files.add(Path(path).parent / catalog_file) for catfile in catalog_files: write_catalog( irimap.copy(), output=catfile, - directory=dir, + directory=directory, append=append_catalog, ) @@ -1074,7 +1075,7 @@ def _save( write_catalog( {self.get_version(as_iri=True): file}, output=catalog_file, - directory=dir, + directory=directory, append=append_catalog, ) @@ -1128,7 +1129,7 @@ def _save( ): graph.remove((s, p, o)) graph.add((rdflib.URIRef(self.iri), p, o)) - graph.serialize(destination=filename, format=format) + graph.serialize(destination=file, format=format) finally: os.remove(tmpfile) @@ -2065,11 +2066,9 @@ def new_annotation_property( def copy(self): """Return a copy of the ontology.""" with tempfile.TemporaryDirectory() as dirname: - filename = os.path.join(dirname, "tmp.owl") self._save( - filename, directory=dirname, - format="rdfxml", + format="turtle", recursive=True, write_catalog_file=True, mkdir=True, @@ -2077,15 +2076,10 @@ def copy(self): # tmpfile, # squash=True, ) - # print(self.TestClass) - # print out content of all files in tmpdir - for file in os.listdir(dirname): - with open(os.path.join(dirname, file), "r") as f: - print(f"File: {file}") - print(f.read()) - - ontology = get_ontology(filename).load() - # print(ontology.TestClass) + ontology = get_ontology(self.base_iri).load( + filename=dirname + "/" + self.name + ".ttl" + ) + ontology.name = self.name return ontology def copy2(self, recursive=True, copy_world=True): @@ -2098,10 +2092,10 @@ def copy2(self, recursive=True, copy_world=True): if copy_world: # new_world = World() - self.world.get_triples() - # raise NotImplementedError( - # "Argument `copy_world` is not yet implemented." - # ) + # self.world.get_triples() + raise NotImplementedError( + "Argument `copy_world` is not yet implemented." + ) onto = Ontology( world=World() if copy_world else self.world, diff --git a/tests/ontopy_tests/test_copy.py b/tests/ontopy_tests/test_copy.py index 0f041e3bf..bd9a26a48 100644 --- a/tests/ontopy_tests/test_copy.py +++ b/tests/ontopy_tests/test_copy.py @@ -20,6 +20,9 @@ def test_copy(testonto: "Ontology") -> None: # Make a copy and check that new entity in original is not in copy testonto_copy = testonto.copy() + # Check that the name of the cipy is the same + assert testonto_copy.name == testonto.name + testonto.new_entity("FantasyClass2", testonto.TestClass) assert testonto.FantasyClass2 @@ -67,7 +70,12 @@ def python_name_triples(ontology): assert len(list(python_name_triples(testonto))) == 3 # save, not keeping python name triples in the saved ontology - testonto.save(tmp_path / "testonto_1.ttl", keep_python_names=False) + testonto.save( + tmp_path / "testonto_1.ttl", + keep_python_names=False, + recursive=True, + write_catalog_file=True, + ) # load the saved ontology and check that python name triples are not there testonto1 = get_ontology(tmp_path / "testonto_1.ttl").load() @@ -94,7 +102,6 @@ def test_copy_emmo(emmo: "Ontology") -> None: from emmopy import get_emmo - # Make a copy and check that new entity in original is not in copy new_emmo = emmo.copy() assert new_emmo == emmo diff --git a/tests/test_excelparser/onto.xlsx b/tests/test_excelparser/onto.xlsx index 3e7a07528cd284e449eb6ac9d8a1b09f38dbb9e3..8e2632e6f0392ee877bbd0e1428549e790c3f48d 100644 GIT binary patch delta 2723 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 delta 2719 zcmZ9OWmFS>8-}+DBH&1K)@j~Lg~RsMMMNq0Y}LG5;D4^ zl$0DP5{gJn*^8d{{5|LWeY!r}AD(lb>$%U5*aYlp0@g6lVTe01?UiQ$0R3|S05bpp zi13pM4-EEo2@LeT9O3Wx)M_SRMimm{=tRA;@b={kcU01K)fOkPB1*)3h6@N38{t%8 ze=hcX(r^SbU&5}|@*O(Et1Nk6Au&Wv;rn1>Oy^53VGa(>D|7D*I2VZ$67%Bg{p8UL zBBtI0i~}?mG4$#XtoEmqqwYaO&zf11CnKQI1o=EnKqIZs^t!VWL}{_6mW4p##RqRz z*stZD>#RSf$@;Gg_N|% z?dg*m=B<<^CSe|aADq`C2g8nTG()I}(e-%^fW4@S#QY&HB%l8`_n5^+PpwAO7Ab2Ri6>% z!t+T%t)7*@Lo=`;=8iog@25F#!y)Ah`JHZg5H6zo~uyX$Mo z!Z;@I%-=--a!-BRJmgph@P-PkZDa5OG4Pi=_bSr>01v>p9VibD0j9&)rq2jLy60j0 z8bWifDQs&sOE0HiTFvHGem*jjO}qB4QMbzK*S0`VTF#To5j*Afed^!>&nh$Bt6_xA zMRSx(!Bg&Je4$ChCM$+=teEGrtmzATW(G5fW@pm5G}n^9lRDdIcrHD5_#al4xo}!& znhIPYHxE%)bDi%+eqsp*%{F1%Rw*%h-_2WHyr7r!Af7bWE*mCIj8KezVXzsM#Bo0| zXp(DCKYx#sm!$F6mmBCP#j z*P;4PZU*E&uEfK-%P;Pb$HllUmCa-qsg-nhD!B|E)^|YRiq|nb>vY&005PG+T&DeHhwlOP8Nc#wPQ8}F|2hTrH-BU{N%acUSQ;Z zmwOS;G<{l@9ea>-EO==rnXD_MA3x$XTv7YH5+UeIR_!3ASt54X>&l&KcYxJ#j2n;L%oCS1d_sPPpRFX#w`wsnq!f9g2eUWyvCO2!#G`KJ+J~=w zCm0r4AnMkOhw_3>;>H=(cs2A$F1Ojr5h&XXf(=X><5$Pm67PQOhX zflRr>SL&!{xfJ51OY19Hcgt(KOm0&Mb2nA;uu+0hJ0*(9VF7RZVU+acj=;mjHoSdw z^$uo;=aFk6=5+-pct(Qi_QArI(m*nhFWCrH?frT*WnD`VU9Q}RFj%?L%aI!thgdZ7HntllmY})R}Qukgbq+cRWw2^$xgw zGTGXzk&gz;)ESBK<=fWt;+hQl`kpJ65pLKB91=U=H*yNNEj%q$ww~$Xy-AY+(gTCN zc4P&RO=GufQYm}z$}f_9D-+@ab>88{(gxOFJ{BYPNgsWbY`~U$C7AogRn^;L6~xgI z7_dUB%*o-1w~}=yZE+bd6DSm&yL}G{;cN`_SE|okpur4PwX_Obc&sTk-9tQnCH2i6 zY3m+sgHF`A8c+N2)6r&4KM@q!a+3ZN#n0upsMnc(jYDjXxt-kGU*(x$8D)0yUPH`Ff2O%gKpc+mWWdQ8Q zC7^VHCb({tI#3-4vcCc%T*0+n5ySb|OM#g6PLU7y+FlXFs*i)}OX47CDGVb!Gy?(vOsDn3@^{u#D#2x-wSj)PA+#3I69;w>Kih5dJMdou!U%r= diff --git a/tests/test_excelparser/test_excelparser.py b/tests/test_excelparser/test_excelparser.py index c3e3affb0..478eddc9a 100644 --- a/tests/test_excelparser/test_excelparser.py +++ b/tests/test_excelparser/test_excelparser.py @@ -11,13 +11,18 @@ from pathlib import Path -@pytest.mark.filterwarnings("ignore:Ignoring concept :UserWarning") -@pytest.mark.filterwarnings("ignore:Invalid parents for :UserWarning") -@pytest.mark.filterwarnings( - "ignore:Not able to add the following concepts :UserWarning" -) -def test_excelparser(repo_dir: "Path") -> None: +# @pytest.mark.filterwarnings("ignore:Ignoring concept :UserWarning") +# @pytest.mark.filterwarnings("ignore:Invalid parents for :UserWarning") +# @pytest.mark.filterwarnings( +# "ignore:Not able to add the following concepts :UserWarning" +# ) + +if True: + # def test_excelparser(repo_dir: "Path") -> None: """Basic test for creating an ontology from an Excel file.""" + import pathlib + + repo_dir = pathlib.Path("/home/flb/projects/Team4.0/EMMOntoPy") ontopath = ( repo_dir / "tests" @@ -35,9 +40,10 @@ def test_excelparser(repo_dir: "Path") -> None: # ontology.save("test.ttl") # used for printing new ontology when debugging # remove python_names added by owlready2 before comparing the ontologies. # The emmontopy.ontology.save removes all triples with this relation as default. - ontology.save("test.ttl") # used for printing new ontology when debugging - - # onto2 = ontology.copy() + # ontology.save("test.ttl") # used for printing new ontology when debugging + print("before copy") + onto2 = ontology.copy() + print("after copy") ontology._del_data_triple_spod( p=ontology._abbreviate( "http://www.lesfleursdunormal.fr/static/_downloads/" diff --git a/tests/test_save.py b/tests/test_save.py index 765aefbd0..5dcc11abd 100755 --- a/tests/test_save.py +++ b/tests/test_save.py @@ -26,7 +26,6 @@ def test_save( testonto.save(tmpdir / "testonto_saved.ttl") # check that the file is in tmpdir assert (tmpdir / "testonto_saved.ttl").exists() - testonto.save(format="rdfxml") # provide a format and filename testonto.save(tmpdir / "testonto_saved.owl", format="rdfxml") @@ -37,6 +36,7 @@ def test_save( # the file will be saved in the current directory testonto.save(format="rdfxml") assert Path(testonto.name + ".rdfxml").exists() + # check if testonto_saved.owl and testonto.rdfxml are identical files with open(tmpdir / "testonto_saved.owl") as f: owlfile = f.read() @@ -47,11 +47,11 @@ def test_save( Path(testonto.name + ".rdfxml").unlink() # Provide format and directory - testonto.save(format="rdfxml", dir=tmpdir) + testonto.save(format="rdfxml", directory=tmpdir) assert (tmpdir / str(testonto.name + ".rdfxml")).exists() # Provide directory that does not exist, but add mkdir=True - testonto.save(format="owl", dir=tmpdir / "subdir", mkdir=True) + testonto.save(format="owl", directory=tmpdir / "subdir", mkdir=True) assert (tmpdir / "subdir" / (testonto.name + ".owl")).exists() # Check that file is overwritten only wityh overwrite=True, and @@ -65,6 +65,9 @@ def test_save( # 4. save testonto to testonto.owl again, but with overwrite=True # 5. check that testonto.owl is the same as testonto_saved.owl # NB! this is not currently working, issue #685 + # It might be that this inetnional behaviour of save should be changed. + # If so, the tests should change accordingly. + # This should be addressed in issue #685 # 1. with open(tmpdir / "testonto_saved.owl") as f: @@ -73,7 +76,7 @@ def test_save( owlfile2 = f.read() assert owlfile == owlfile2 # 2. - testonto.save(format="rdfxml", dir=tmpdir) + testonto.save(format="rdfxml", directory=tmpdir) # 3. with open(tmpdir / "testonto_saved.owl") as f: owlfile = f.read() @@ -81,7 +84,7 @@ def test_save( owlfile2 = f.read() # assert owlfile != owlfile2 # to be uncommented when issue #685 is fixed # 4. - testonto.save(format="rdfxml", dir=tmpdir, overwrite=True) + testonto.save(format="rdfxml", directory=tmpdir, overwrite=True) # 5. with open(tmpdir / "testonto_saved.owl") as f: owlfile = f.read() @@ -89,13 +92,23 @@ def test_save( owlfile2 = f.read() assert owlfile == owlfile2 - # Test that the ontology is saved recursively when deisred + # Test that the ontology is saved recursively only when desired testonto.save( - format="ttl", dir=tmpdir / "recursively", mkdir=True, recursive=True + format="ttl", + directory=tmpdir / "recursively", + mkdir=True, + recursive=False, ) assert (tmpdir / "recursively" / "testonto.ttl").exists() - # Recursive save is not working . Issue #687 - # assert (tmpdir / "recursively" / "models.ttl").exists() + assert (tmpdir / "recursively" / "models.ttl").exists() == False + + testonto.save( + format="ttl", + directory=tmpdir / "recursively", + mkdir=True, + recursive=True, + ) + assert (tmpdir / "recursively" / "models.ttl").exists() # squash merge during save @@ -107,17 +120,99 @@ def test_save( # Simple working tests without pytest getting in the way - feel free to change to pytest - -if True: # Whether to test for EMMO +def test_save_emmo( + tmpdir: "Path", + repo_dir: "Path", +) -> None: + import os from pathlib import Path - from emmopy import get_emmo + from ontopy import get_ontology + + # For debugging purposes tmpdir can be set to a directory + # in the current directory: test_save_dir + # Remember to remove the directory after testing + debug = False + if debug: + tmpdir = repo_dir / "tests" / "test_save_dir" + import os + + os.makedirs(tmpdir, exist_ok=True) + emmo = get_ontology( + "https://raw.githubusercontent.com/emmo-repo/EMMO/1.0.0-beta4/emmo.ttl" + ).load() + + # Since version is missing in some imported ontologies (at least in periodic_table) + # we need to fix that. + # Note that ths is fix of an error in EMMO-1.0.0-beta4 + version = emmo.get_version() + # for onto in emmo.indirectly_imported_ontologies(): + # try: + # onto.get_version(as_iri=True) + # except TypeError: + # onto.set_version(version) + # # print(onto, onto.get_version(as_iri=True)) - emmo = get_emmo() emmo.save( format="turtle", - dir=Path(__file__).absolute().parent / "outdir", + directory=tmpdir / "emmosaved", + recursive=True, + mkdir=True, + write_catalog_file=True, + keep_python_names=False, + ) + assert set(os.listdir(tmpdir / "emmosaved")) == { + "catalog-v001.xml", + "disciplines", + "emmo.ttl", + "mereocausality", + "multiperspective", + "perspectives", + } + + assert set(os.listdir(tmpdir / "emmosaved" / "disciplines")) == { + "materials.ttl", + "math.ttl", + "computerscience.ttl", + "chemistry.ttl", + "unitsextension.ttl", + "catalog-v001.xml", + "isq.ttl", + "periodictable.ttl", + "metrology.ttl", + "siunits.ttl", + "disciplines.ttl", + "manufacturing.ttl", + "models.ttl", + } + + +if True: + # def test_save_emmo_domain_ontology(): + import os + from pathlib import Path + + from ontopy import get_ontology + + # For debugging purposes tmpdir can be set to a directory + # in the current directory: test_save_dir + # Remember to remove the directory after testing + debug = False + if debug: + tmpdir = repo_dir / "tests" / "test_save_dir" + import os + + os.makedirs(tmpdir, exist_ok=True) + + onto = get_ontology( + "https://raw.githubusercontent.com/emmo-repo/domain-electrochemistry/master/electrochemistry.ttl" + ).load() + + onto.save( + format="turtle", + directory="saved", recursive=True, mkdir=True, write_catalog_file=True, + keep_python_names=True, )