Skip to content

Commit 7de0927

Browse files
authored
Merge pull request #582 from emmo-repo/update-get-by-label
Updated get_by_label() so that it now accepts label, name and full iri
2 parents e10ed71 + 7a2b1ac commit 7de0927

File tree

4 files changed

+144
-51
lines changed

4 files changed

+144
-51
lines changed

ontopy/excelparser.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from ontopy import get_ontology
2222
from ontopy.utils import EMMOntoPyException, NoSuchLabelError
2323
from ontopy.utils import ReadCatalogError, read_catalog
24+
from ontopy.ontology import LabelDefinitionError
2425
from ontopy.manchester import evaluate
2526
import owlready2 # pylint: disable=C0411
2627

@@ -276,7 +277,12 @@ def create_ontology_from_pandas( # pylint:disable=too-many-locals,too-many-bran
276277
if not parents:
277278
parents = [owlready2.Thing]
278279

279-
concept = onto.new_entity(name, parents)
280+
try:
281+
concept = onto.new_entity(name, parents)
282+
except LabelDefinitionError:
283+
concepts_with_errors["wrongly_defined"].append(name)
284+
continue
285+
280286
added_rows.add(index)
281287
# Add elucidation
282288
try:

ontopy/ontology.py

+85-50
Original file line numberDiff line numberDiff line change
@@ -186,20 +186,28 @@ def __init__(self, *args, **kwargs):
186186
doc="Whether to include imported ontologies in dir() listing.",
187187
)
188188

189+
# Other settings
190+
_colon_in_label = False
191+
colon_in_label = property(
192+
fget=lambda self: self._colon_in_label,
193+
fset=lambda self, v: setattr(self, "_colon_in_label", bool(v)),
194+
doc="Whether to accept colon in name-part of IRI. "
195+
"If true, the name cannot be prefixed.",
196+
)
197+
189198
def __dir__(self):
190-
set_dir = set(super().__dir__())
199+
dirset = set(super().__dir__())
191200
lst = list(self.get_entities(imported=self._dir_imported))
192201
if self._dir_preflabel:
193-
set_dir.update(
202+
dirset.update(
194203
_.prefLabel.first() for _ in lst if hasattr(_, "prefLabel")
195204
)
196205
if self._dir_label:
197-
set_dir.update(_.label.first() for _ in lst if hasattr(_, "label"))
206+
dirset.update(_.label.first() for _ in lst if hasattr(_, "label"))
198207
if self._dir_name:
199-
set_dir.update(_.name for _ in lst if hasattr(_, "name"))
200-
201-
set_dir.difference_update({None}) # get rid of possible None
202-
return sorted(set_dir)
208+
dirset.update(_.name for _ in lst if hasattr(_, "name"))
209+
dirset.difference_update({None}) # get rid of possible None
210+
return sorted(dirset)
203211

204212
def __getitem__(self, name):
205213
item = super().__getitem__(name)
@@ -257,7 +265,12 @@ def get_unabbreviated_triples(
257265
)
258266

259267
def get_by_label(
260-
self, label: str, label_annotations: str = None, prefix: str = None
268+
self,
269+
label: str,
270+
label_annotations: str = None,
271+
prefix: str = None,
272+
imported: bool = True,
273+
colon_in_label: bool = None,
261274
):
262275
"""Returns entity with label annotation `label`.
263276
@@ -272,50 +285,52 @@ def get_by_label(
272285
the base iri of an ontology (with trailing slash (/) or hash
273286
(#) stripped off). The search for a matching label will be
274287
limited to this namespace.
288+
imported: Whether to also look for `label` in imported ontologies.
289+
colon_in_label: Whether to accept colon (:) in a label or name-part
290+
of IRI. Defaults to the `colon_in_label` property of `self`.
291+
Setting this true cannot be combined with `prefix`.
275292
276293
If several entities have the same label, only the one which is
277294
found first is returned.Use get_by_label_all() to get all matches.
278295
279-
A NoSuchLabelError is raised if `label` cannot be found.
296+
Note, if different prefixes are provided in the label and via
297+
the `prefix` argument a warning will be issued and the
298+
`prefix` argument will take precedence.
280299
281-
Note
282-
----
283-
The current implementation also supports "*" as a wildcard
284-
matching any number of characters. This may change in the future.
300+
A NoSuchLabelError is raised if `label` cannot be found.
285301
"""
286-
# pylint: disable=too-many-arguments,too-many-branches
302+
# pylint: disable=too-many-arguments,too-many-branches,invalid-name
287303
if not isinstance(label, str):
288304
raise TypeError(
289305
f"Invalid label definition, must be a string: {label!r}"
290306
)
291-
if " " in label:
292-
raise ValueError(
293-
f"Invalid label definition, {label!r} contains spaces."
294-
)
307+
295308
if self._label_annotations is None:
296309
for iri in DEFAULT_LABEL_ANNOTATIONS:
297310
try:
298311
self.add_label_annotation(iri)
299312
except ValueError:
300313
pass
301314

302-
splitlabel = label.split(":", 1)
303-
if len(splitlabel) > 2:
304-
raise ValueError(
305-
f"Invalid label definition, {label!r}"
306-
" contains more than one ':' ."
307-
"The string before ':' indicates the prefix. "
308-
"The string after ':' indicates the label."
309-
)
310-
if len(splitlabel) == 2:
311-
label = splitlabel[1]
312-
if prefix and prefix != splitlabel[0]:
313-
warnings.warn(
314-
f"Prefix given both as argument ({prefix}) "
315-
f"and in label ({splitlabel[0]}). "
316-
"Prefix given in label takes presendence "
315+
if colon_in_label is None:
316+
colon_in_label = self._colon_in_label
317+
if colon_in_label:
318+
if prefix:
319+
raise ValueError(
320+
"`prefix` cannot be combined with `colon_in_label`"
317321
)
318-
prefix = splitlabel[0]
322+
else:
323+
splitlabel = label.split(":", 1)
324+
if len(splitlabel) == 2 and not splitlabel[1].startswith("//"):
325+
label = splitlabel[1]
326+
if prefix and prefix != splitlabel[0]:
327+
warnings.warn(
328+
f"Prefix given both as argument ({prefix}) "
329+
f"and in label ({splitlabel[0]}). "
330+
"Prefix given in argument takes presendence "
331+
)
332+
if not prefix:
333+
prefix = splitlabel[0]
319334

320335
if prefix:
321336
entitylist = self.get_by_label_all(
@@ -327,36 +342,56 @@ def get_by_label(
327342
return entitylist[0]
328343

329344
raise NoSuchLabelError(
330-
f"No label annotations matches {label!r} with prefix "
345+
f"No label annotations matches {label!r} with prefix "
331346
f"{prefix!r}"
332347
)
333-
# if label in self._namespaces:
334-
# return self._namespaces[label]
335348

336-
if label_annotations is None:
337-
annotations = (a.name for a in self.label_annotations)
338-
else:
339-
annotations = (
340-
a.name if hasattr(a, "storid") else a for a in label_annotations
341-
)
342-
for key in annotations:
343-
entity = self.search_one(**{key: label})
344-
if entity:
345-
return entity
349+
# Label is a full IRI
350+
entity = self.world[label]
351+
if entity:
352+
return entity
353+
354+
# First entity with matching label annotation
355+
annotation_ids = (
356+
(self._abbreviate(ann, False) for ann in label_annotations)
357+
if label_annotations
358+
else (ann.storid for ann in self.label_annotations)
359+
)
360+
get_triples = (
361+
self.world._get_data_triples_spod_spod
362+
if imported
363+
else self._get_data_triples_spod_spod
364+
)
365+
for annotation_id in annotation_ids:
366+
for s, _, _, _ in get_triples(None, annotation_id, label, None):
367+
return self.world[self._unabbreviate(s)]
346368

369+
# Special labels
347370
if self._special_labels and label in self._special_labels:
348371
return self._special_labels[label]
349372

373+
# Check if label is a name under base_iri
350374
entity = self.world[self.base_iri + label]
351375
if entity:
352376
return entity
353377

354-
raise NoSuchLabelError(f"No label annotations matches {label!r}")
378+
# Check if label is a name in any namespace
379+
for namespace in self._namespaces.keys():
380+
entity = self.world[namespace + label]
381+
if entity:
382+
return entity
383+
384+
raise NoSuchLabelError(f"No label annotations matches '{label}'")
355385

356386
def get_by_label_all(self, label, label_annotations=None, prefix=None):
357387
"""Like get_by_label(), but returns a list with all matching labels.
358388
359389
Returns an empty list if no matches could be found.
390+
391+
Note
392+
----
393+
The current implementation also supports "*" as a wildcard
394+
matching any number of characters. This may change in the future.
360395
"""
361396
if not isinstance(label, str):
362397
raise TypeError(
@@ -1582,7 +1617,7 @@ def new_entity(
15821617
15831618
Throws exception if name consists of more than one word.
15841619
"""
1585-
if len(name.split(" ")) > 1:
1620+
if " " in name:
15861621
raise LabelDefinitionError(
15871622
f"Error in label name definition '{name}': "
15881623
f"Label consists of more than one word."
@@ -1684,7 +1719,7 @@ def _get_unabbreviated_triples(
16841719
_unabbreviate(self, p, blank=blank),
16851720
_unabbreviate(self, o, blank=blank),
16861721
)
1687-
for s, p, o, d in self._get_data_triples_spod_spod(*abb, d=""):
1722+
for s, p, o, d in self._get_data_triples_spod_spod(*abb, d=None):
16881723
yield (
16891724
_unabbreviate(self, s, blank=blank),
16901725
_unabbreviate(self, p, blank=blank),

tests/test_dir.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from pathlib import Path
2+
3+
from ontopy import get_ontology
4+
5+
6+
thisdir = Path(__file__).resolve().parent
7+
8+
onto = get_ontology(
9+
thisdir / "test_excelparser/imported_onto/ontology.ttl"
10+
).load()
11+
onto.dir_imported = False
12+
onto.dir_preflabel = False
13+
onto.dir_label = False
14+
onto.dir_name = False
15+
assert "TestClass2" not in dir(onto)
16+
17+
onto.dir_imported = True
18+
onto.dir_preflabel = True
19+
assert onto._dir_imported
20+
assert onto.TestClass2
21+
assert "TestClass2" in dir(onto)
22+
assert "testclass" not in dir(onto)
23+
assert "testclass2" not in dir(onto)
24+
25+
onto.dir_name = True
26+
assert "TestClass2" in dir(onto)
27+
assert "testclass" in dir(onto)
28+
assert "testclass2" in dir(onto)

tests/test_get_by_label.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import pytest
2+
3+
from ontopy import get_ontology
4+
from ontopy.ontology import NoSuchLabelError
5+
6+
7+
# Loading emmo-inferred where everything is sqashed into one ontology
8+
emmo = get_ontology().load()
9+
assert emmo[emmo.Atom.name] == emmo.Atom
10+
assert emmo[emmo.Atom.iri] == emmo.Atom
11+
12+
# Load an ontology with imported sub-ontologies
13+
onto = get_ontology(
14+
"https://raw.githubusercontent.com/BIG-MAP/BattINFO/master/battinfo.ttl"
15+
).load()
16+
assert onto.Electrolyte.prefLabel.first() == "Electrolyte"
17+
18+
19+
# Check colon_in_name argument
20+
onto.Atom.altLabel.append("Element:X")
21+
with pytest.raises(NoSuchLabelError):
22+
onto.get_by_label("Element:X")
23+
24+
assert onto.get_by_label("Element:X", colon_in_label=True) == onto.Atom

0 commit comments

Comments
 (0)