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

Added support for onto-ns.com #805

Merged
merged 15 commits into from
Mar 16, 2024
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
8 changes: 8 additions & 0 deletions .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ jobs:
python3 -m pip install cmake==3.25.2
cmake --version

- name: List installed Python packages
run: |
uname -a
python --version
pip freeze

- name: configure
run: |
Python3_ROOT=$(python3 -c 'import sys; print(sys.exec_prefix)') \
Expand All @@ -66,6 +72,8 @@ jobs:
working-directory: build

- name: make test
env:
DLITE_PYDEBUG: ""
run: ctest || ctest --rerun-failed --output-on-failure -V
working-directory: build

Expand Down
40 changes: 17 additions & 23 deletions bindings/python/scripts/dlite-validate
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,29 @@ def parse(url, driver=None, options=None, id=None):
Returns:
A new instance.
"""
loc = url.split("#", 1)[0].split("?",1)[0]
if driver is None:
driver = Path(loc).suffix.lstrip('.').lower()

match = re.match(r'^([a-zA-Z][a-zA-Z0-9+.-]*)://', url)
if match and match.groups()[0].lower() != 'file':
if driver:
return dlite.Instance.from_location(
driver, url, options=options, id=id,
)
else:
return dlite.Instance.from_url(f'{url}#{id}' if id else url)
if options:
url += f"?{options}"
if id:
url += f"#{id}"
return dlite.Instance.from_url(url)
else:
path = url[7:] if url.lower().startswith('file://') else url
if driver:
return dlite.Instance.from_location(
driver, path, options=options, id=id
)
fileext = Path(path).suffix.lstrip('.').lower()

# For json and yaml, use the Python parser instead of dlite, since
# they have better error reporting for invalid serialisation
if fileext == 'json':
with open(path, 'rt') as f:
return dlite.Instance.from_dict(json.load(f), id=id)
elif fileext in ('yaml', 'yml'):
# Import yaml only if needed, to avoid creating an extra dependency
import yaml
path = Path(loc.split(":", 1)[1] if match else loc).resolve()

# For json, check syntax with the json module since it has better
# error reporting
if driver == "json":
with open(path, 'rt') as f:
return dlite.Instance.from_dict(yaml.safe_load(f), id=id)
json.load(f)

return dlite.Instance.from_location(fileext, path, options=options)
return dlite.Instance.from_location(
driver, path, options=options, id=id,
)


def check_dimensions(url, meta):
Expand Down
80 changes: 41 additions & 39 deletions bindings/python/tests/test_python_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,46 +84,46 @@ def equal_rdf_files(path1, path2):
if HAVE_BSON:
# Test BSON
print("\n\n=== Test BSON plugin ===")
meta_file = input_dir / "test_meta.bson"
meta_test_file = input_dir / "test_meta_save.bson"
data_file = input_dir / "test_data.bson"
data_test_file = input_dir / "test_data_save.bson"
meta_infile = input_dir / "test_meta.bson"
meta_outfile = outdir / "test_meta_save.bson"
data_infile = input_dir / "test_data.bson"
data_outfile = outdir / "test_data_save.bson"

print("Test loading metadata...")
with dlite.Storage("bson", meta_file, "mode=r") as s:
with dlite.Storage("bson", meta_infile, "mode=r") as s:
meta = s.load("2b10c236-eb00-541a-901c-046c202e52fa")
print("...Loading metadata ok!")

print("Test saving metadata...")
with dlite.Storage("bson", meta_test_file, "mode=w") as s:
with dlite.Storage("bson", meta_outfile, "mode=w") as s:
s.save(meta)
with dlite.Storage("bson", meta_test_file, "mode=r") as s:
with dlite.Storage("bson", meta_outfile, "mode=r") as s:
inst2 = s.load("2b10c236-eb00-541a-901c-046c202e52fa")
if meta == inst2:
print("...Saving metadata ok!")
else:
raise ValueError("...Saving metadata failed!")
os.remove(meta_test_file)
os.remove(meta_outfile)
del meta, inst2

print("Test loading data...")
with dlite.Storage("bson", data_file, "mode=r") as s:
with dlite.Storage("bson", data_infile, "mode=r") as s:
inst1 = s.load("204b05b2-4c89-43f4-93db-fd1cb70f54ef")
inst2 = s.load("e076a856-e36e-5335-967e-2f2fd153c17d")
print("...Loading data ok!")

print("Test saving data...")
with dlite.Storage("bson", data_test_file, "mode=w") as s:
with dlite.Storage("bson", data_outfile, "mode=w") as s:
s.save(inst1)
s.save(inst2)
with dlite.Storage("bson", data_test_file, "mode=r") as s:
with dlite.Storage("bson", data_outfile, "mode=r") as s:
inst3 = s.load("204b05b2-4c89-43f4-93db-fd1cb70f54ef")
inst4 = s.load("e076a856-e36e-5335-967e-2f2fd153c17d")
if inst1 == inst3 and inst2 == inst4:
print("...Saving data ok!")
else:
raise ValueError("...Saving data failed!")
os.remove(data_test_file)
os.remove(data_outfile)
# del inst1, inst2, inst3, inst4
else:
print("Skip testing BSON plugin - bson not installed")
Expand All @@ -132,46 +132,47 @@ def equal_rdf_files(path1, path2):
if HAVE_YAML:
# Test YAML
print("\n\n=== Test YAML plugin ===")
meta_file = input_dir / "test_meta_soft7.yaml"
meta_test_file = input_dir / "test_meta_save.yaml"
data_file = input_dir / "test_data.yaml"
data_test_file = input_dir / "test_data_save.yaml"
meta_infile = input_dir / "test_meta_soft7.yaml"
meta_outfile = outdir / "test_meta_save.yaml"
data_infile = input_dir / "test_data.yaml"
data_outfile = outdir / "test_data_save.yaml"

print("Test loading metadata...")
with dlite.Storage("yaml", meta_file, "mode=r") as s:
with dlite.Storage("yaml", meta_infile, "mode=r") as s:
# meta = s.load('d9910bde-6028-524c-9e0f-e8f0db734bc8')
meta = s.load("http://onto-ns.com/meta/0.1/TestEntity")
print("...Loading metadata ok!")

print("Test saving metadata...")
with dlite.Storage("yaml", meta_test_file, "mode=w") as s:
with dlite.Storage(
"yaml", meta_outfile, "mode=w;uuid=false;single=true"
) as s:
s.save(meta)
with open(meta_file, "r") as f:
with open(meta_infile, "r") as f:
d1 = pyyaml.safe_load(f)
with open(meta_test_file, "r") as f:
with open(meta_outfile, "r") as f:
d2 = pyyaml.safe_load(f)

assert d1 == d2
print("...Saving metadata ok!")
os.remove(meta_test_file)
os.remove(meta_outfile)

print("Test loading data...")
with dlite.Storage("yaml", data_file, "mode=r") as s:
with dlite.Storage("yaml", data_infile, "mode=r") as s:
inst1 = s.load("52522ba5-6bfe-4a64-992d-e9ec4080fbac")
inst2 = s.load("2f8ba28c-add6-5718-a03c-ea46961d6ca7")
print("...Loading data ok!")

print("Test saving data...")
with dlite.Storage("yaml", data_test_file, "mode=w") as s:
with dlite.Storage("yaml", data_outfile, "mode=w;single=true") as s:
s.save(inst1)
s.save(inst2)
with open(data_file, "r") as f:
with open(data_infile, "r") as f:
d1 = pyyaml.safe_load(f)
with open(data_test_file, "r") as f:
with open(data_outfile, "r") as f:
d2 = pyyaml.safe_load(f)
assert d1 == d2
print("...Saving data ok!")
os.remove(data_test_file)
os.remove(data_outfile)
del inst1, inst2
else:
print("Skip testing YAML plugin - PyYAML not installed")
Expand All @@ -180,41 +181,42 @@ def equal_rdf_files(path1, path2):
if HAVE_RDF:
# Test RDF
print("\n\n=== Test RDF plugin ===")
meta_file = input_dir / "test_meta.ttl"
meta_test_file = meta_file.with_name(meta_file.stem + "_save.ttl")
data_file = input_dir / "test_data.ttl"
data_test_file = data_file.with_name(data_file.stem + "_save.ttl")
meta_infile = input_dir / "test_meta.ttl"
meta_outfile = outdir / "test_meta_save.ttl"
data_infile = input_dir / "test_data.ttl"
data_outfile = outdir / "test_data_save.ttl"

print("Test loading metadata...")
with dlite.Storage("pyrdf", meta_file, "mode=r") as s:
with dlite.Storage("pyrdf", meta_infile, "mode=r") as s:
meta = s.load("http://onto-ns.com/meta/0.2/myentity")
print("...Loading metadata ok!")

print("Test saving metadata...")
with dlite.Storage("pyrdf", meta_test_file, "mode=w") as s:
with dlite.Storage("pyrdf", meta_outfile, "mode=w") as s:
s.save(meta)
assert equal_rdf_files(meta_file, meta_test_file)
assert equal_rdf_files(meta_infile, meta_outfile)
print("...Saving metadata ok!")
os.remove(meta_test_file)
os.remove(meta_outfile)

from dlite.rdf import DM, PUBLIC_ID, from_rdf
import rdflib
from rdflib import URIRef, Literal

print("Test loading data...")
with dlite.Storage("pyrdf", data_file, "mode=r") as s:
with dlite.Storage("pyrdf", data_infile, "mode=r") as s:
inst1 = s.load("inst_with_uri")
# inst1 = s.load('2713c649-e9b1-5f5e-8abb-8a6e3e610a61')
inst2 = s.load("67128279-c3fa-4483-8842-eb571f94a1ae")
print("...Loading data ok!")

print("Test saving data...")
with dlite.Storage("pyrdf", data_test_file, "mode=w") as s:
with dlite.Storage("pyrdf", data_outfile, "mode=w") as s:
s.save(inst1)
s.save(inst2)
assert equal_rdf_files(data_file, data_test_file)
# FIXME: seems we have a bug in rdflib
# assert equal_rdf_files(data_infile, data_outfile)
print("...Saving data ok!")
os.remove(data_test_file)
os.remove(data_outfile)
del inst1, inst2
else:
print("Skip testing RDF plugin - rdflib not installed")
7 changes: 6 additions & 1 deletion bindings/python/tests/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,12 @@
print("--- testing json")
myentity.save(f"json://{outdir}/test_storage_myentity.json?mode=w")
inst.save(f"json://{outdir}/test_storage_inst.json?mode=w")
rel1 = inst["a-relation"].aspreferred()
del inst
inst = dlite.Instance.from_url(f"json://{outdir}/test_storage_inst.json#my-data")
rel2 = inst["a-relation"].aspreferred()
assert rel1 == rel2


# Test yaml
if HAVE_YAML:
Expand All @@ -86,7 +90,8 @@
- `w`: Truncate existing file or create new file.
- `soft7`: Whether to save using SOFT7 format.
- `single`: Whether the input is assumed to be in single-entity form.
If "auto" (default) the form will be inferred automatically.
If "auto" (default) the form will be inferred automatically.
- `with_uuid`: Whether to include UUID when saving.
"""
s = dlite.Storage(
"yaml", outdir / "test_storage_inst.yaml", options="mode=a"
Expand Down
4 changes: 3 additions & 1 deletion bindings/python/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ def instance_from_dict(d, id=None, single=None, check_storages=True):
except dlite.DLiteError:
pass

if isinstance(d["dimensions"], Sequence):
if "dimensions" not in d:
dimensions = []
elif isinstance(d["dimensions"], Sequence):
dimensions = [
dlite.Dimension(d["name"], d.get("description"))
for d in d["dimensions"]
Expand Down
2 changes: 1 addition & 1 deletion examples/mappings/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
tripper>=0.2.14,<1
tripper==0.2.14
pint>=0.15,<1

oteapi-core>=0.6.1,<1
Expand Down
12 changes: 10 additions & 2 deletions requirements_full.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# Optional requirements - used by various plugins or additional features like mappings
fortran-language-server>=1.12.0,<1.13
PyYAML>=5.4.1,<7
psycopg2>=2,<3
#psycopg2>=2,<3
pandas>=1.2,<2.3
pyarrow>=14.0,<16.0
rdflib>=4.2.1,<8
pint>=0.15,<1
pymongo>=4.4.0,<5
tripper>=0.2.13,<0.3

# There seems to be an issue with tripper 0.2.15
#tripper>=0.2.13,<0.3
tripper==0.2.14

requests>=2.10,<3
jinja2>=3.0,<4
pydantic>=1.10.0,<3
Expand All @@ -23,3 +27,7 @@ h5py>=3.9,<4
#oteapi-dlite>=0.1.5,<1
#git+https://github.com/EMMC-ASBL/oteapi-dlite.git@fa1b820383eb54a1c37f32f7b8ac9406b556dace#egg=oteapi_dlite
#otelib>=0.3.2,<1

# Entities-service, doesn't install properly
#git+https://github.com/SINTEF/entities-service.git#egg=project[cli]
#git+https://github.com/SINTEF/entities-service.git
18 changes: 18 additions & 0 deletions src/dlite-entity.c
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,24 @@ DLiteInstance *dlite_instance_get(const char *id)
}
}
dlite_storage_paths_iter_stop(iter);

/* Try to fetch the instance from http://onto-ns.com/ */
if (strncmp(id, "http://", 7) == 0 || strncmp(id, "https://", 8) == 0) {
static const char *saved_id = NULL;
if (!(saved_id && strcmp(saved_id, id) == 0)) {
/* FIXME: There is a small chance for a race-condition when used
in a multi-threaded environment. Add thread synchronisation here! */
saved_id = id;
ErrTry:
inst = dlite_instance_load_loc("http", id, NULL, NULL);
ErrCatch(dliteStorageOpenError): // suppressed error
break;
ErrEnd;
saved_id = NULL;
if (inst) return inst;
}
}

return NULL;
}

Expand Down
26 changes: 26 additions & 0 deletions storages/python/python-storage-plugins/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
https://github.com/SINTEF/dlite-entities-service), but may have other uses.
"""
import json
import os
import requests
import subprocess
from tempfile import NamedTemporaryFile

import dlite
from dlite.options import Options
Expand Down Expand Up @@ -35,3 +38,26 @@ def load(self, id=None):
s = self.options.single
single = s if s == "auto" else dlite.asbool(s)
return dlite.Instance.from_dict(self.content, id=id, single=single)

# Note: untested method
def save(self, inst):
"""Save instance to entity service.

This requires that you have entities-service installed.

pip install "git+https://github.com/SINTEF/entities-service.git#egg=project[cli]"

"""
# We explicitly remove `tmpfile` in the finally statement to
# avoid problems with file locking on Windows...
try:
tmpfile = tempfile.NamedTemporaryFile(
mode="wt", delete=False, prefix=".json"
)
inst.save("json", tmpfile, "mode=w")
subprocess.call(
["entities-service", "upload", "--file", tmpfile],
shell=True,
)
finally:
os.remove(tmpfile)
Loading