diff --git a/ontopy/ontology.py b/ontopy/ontology.py index bddfbdb6..fa8a46d8 100644 --- a/ontopy/ontology.py +++ b/ontopy/ontology.py @@ -312,33 +312,42 @@ def get_by_label( *, label_annotations: str = None, prefix: str = None, + namespace: str = None, imported: bool = True, colon_in_label: bool = None, ): """Returns entity with label annotation `label`. Arguments: - label: label so search for. - May be written as 'label' or 'prefix:label'. - get_by_label('prefix:label') == - get_by_label('label', prefix='prefix'). - label_annotations: a sequence of label annotation names to look up. - Defaults to the `label_annotations` property. - prefix: if provided, it should be the last component of - the base iri of an ontology (with trailing slash (/) or hash - (#) stripped off). The search for a matching label will be - limited to this namespace. - imported: Whether to also look for `label` in imported ontologies. - colon_in_label: Whether to accept colon (:) in a label or name-part - of IRI. Defaults to the `colon_in_label` property of `self`. - Setting this true cannot be combined with `prefix`. + label: label so search for. + May be written as 'label' or 'prefix:label' + get_by_label('prefix:label') is equivalent to + searching for both + get_by_label('label', prefix='prefix') and + get_by_label('label', namespace='prefix') + label_annotations: a sequence of label annotation names to look up. + Defaults to the `label_annotations` property. + prefix: if provided, it should be the last component of + the base iri of an ontology (with trailing slash (/) or hash + (#) stripped off). The search for a matching label will be + limited to this ontology. `prefix` is ignored if `namespace` + is provided. + namespace: if provided, it should be the namespace of the entity. + It can be provided either as the whole namespace or only as the + last 'word' in the namespace, i.e. what is defined as the + namespace.name. `namespace` takes precedence over `prefix`. + imported: Whether to also look for `label` in imported ontologies. + colon_in_label: Whether to accept colon (:) in a label or name-part + of IRI. Defaults to the `colon_in_label` property of `self`. + Setting this true cannot be combined with `prefix`. If several entities have the same label, only the one which is found first is returned.Use get_by_label_all() to get all matches. - Note, if different prefixes are provided in the label and via - the `prefix` argument a warning will be issued and the - `prefix` argument will take precedence. + Note, if namespace is provided directly in the first argument as + 'namespace:label' it will be used as namespace and + any value given to the `namespace` argument will be + ignored. A NoSuchLabelError is raised if `label` cannot be found. """ @@ -353,23 +362,35 @@ def get_by_label( if colon_in_label is None: colon_in_label = self._colon_in_label - if colon_in_label: - if prefix: - raise ValueError( - "`prefix` cannot be combined with `colon_in_label`" + if colon_in_label and prefix: + raise ValueError( + "`prefix` cannot be combined with `colon_in_label`" + ) + + splitlabel = label.split(":", 1) + if len(splitlabel) == 2 and not splitlabel[1].startswith("//"): + label = splitlabel[1] + if namespace and namespace != splitlabel[0]: + warnings.warn( + f"Namespace given both as argument ({namespace}) " + f"and in label as ({splitlabel[0]}). " + "Namespace given in argument is ignored. " ) - else: - splitlabel = label.split(":", 1) - if len(splitlabel) == 2 and not splitlabel[1].startswith("//"): - label = splitlabel[1] - if prefix and prefix != splitlabel[0]: - warnings.warn( - f"Prefix given both as argument ({prefix}) " - f"and in label ({splitlabel[0]}). " - "Prefix given in argument takes precedence. " - ) - if not prefix: - prefix = splitlabel[0] + namespace = splitlabel[0] + #print('namespace', namespace) + if namespace: + #print('in get_by_label namespavce') + entityset = self.get_by_label_all( + label, + label_annotations=label_annotations, + namespace=namespace, + ) + if len(entityset) == 1: + return entityset.pop() + raise NoSuchLabelError( + f"No label annotations matches for '{label}' " + f"with namespace '{namespace}'." + ) if prefix: entityset = self.get_by_label_all( @@ -379,11 +400,6 @@ def get_by_label( ) if len(entityset) == 1: return entityset.pop() - if len(entityset) > 1: - raise AmbiguousLabelError( - f"Several entities have the same label '{label}' " - f"with prefix '{prefix}'." - ) raise NoSuchLabelError( f"No label annotations matches for '{label}' " f"with prefix '{prefix}'." @@ -425,24 +441,30 @@ def get_by_label_all( label, label_annotations=None, prefix=None, + namespace=None, exact_match=False, ) -> "Set[Optional[owlready2.entity.EntityClass]]": """Returns set of entities with label annotation `label`. Arguments: - label: label so search for. - May be written as 'label' or 'prefix:label'. Wildcard matching - using glob pattern is also supported if `exact_match` is set to - false. - label_annotations: a sequence of label annotation names to look up. - Defaults to the `label_annotations` property. - prefix: if provided, it should be the last component of - the base iri of an ontology (with trailing slash (/) or hash - (#) stripped off). The search for a matching label will be - limited to this namespace. - exact_match: Do not treat "*" and brackets as special characters - when matching. May be useful if your ontology has labels - containing such labels. + label: label so search for. + May be written as 'label' or 'prefix:label'. Wildcard matching + using glob pattern is also supported if `exact_match` is set to + false. + label_annotations: a sequence of label annotation names to look up. + Defaults to the `label_annotations` property. + prefix: if provided, it should be the last component of + the base iri of an ontology (with trailing slash (/) or hash + (#) stripped off). The search for a matching label will be + limited to this ontology. `prefix` is ignored if `namespace` + is provided. + namespace: if provided, it should be the namespace of the entity. + It can be provided either as the whole namespace or only as the + last 'word' in the namespace, i.e. what is defined as the + namespace.name. `namespace` takes precedence over `prefix`. + exact_match: Do not treat "*" and brackets as special characters + when matching. May be useful if your ontology has labels + containing such labels. Returns: Set of all matching entities or an empty set if no matches @@ -496,6 +518,19 @@ def get_by_label_all( entities.update( ent for ent in self.get_entities() if ent.name in matches ) + if namespace: + #print("in namespace", namespace) + #print("entities", entities) + #for ent in entities: + # print("ent.namespace", ent.namespace) + # print("ent.namespace.name", ent.namespace.name) + # print("namespace", namespace) + # print(namespace in [ent.namespace.name, ent.namespace.base_iri]) + return set( + ent + for ent in entities + if namespace in [ent.namespace.name, ent.namespace._base_iri] + ) if prefix: return set( @@ -503,6 +538,7 @@ def get_by_label_all( for ent in entities if ent.namespace.ontology.prefix == prefix ) + return entities def _to_storids(self, sequence, create_if_missing=False): diff --git a/tests/ontopy_tests/test_prefix.py b/tests/ontopy_tests/test_prefix.py index d02e4312..d9bc24ac 100644 --- a/tests/ontopy_tests/test_prefix.py +++ b/tests/ontopy_tests/test_prefix.py @@ -3,12 +3,19 @@ from ontopy.utils import NoSuchLabelError import warnings -if TYPE_CHECKING: - from pathlib import Path +from pathlib import Path -def test_prefix(testonto: "Ontology", emmo: "Ontology") -> None: +def test_prefix() -> None: """Test prefix in ontology""" + from emmopy import get_emmo + from ontopy import get_ontology + + emmo = get_emmo() + repo_dir = Path(__file__).resolve().parent.parent.parent + onto_dir = repo_dir / "tests" / "testonto" + + testonto = get_ontology(onto_dir / "testonto.ttl").load() print(testonto.get_by_label_all("*")) assert len(testonto.get_by_label_all("*")) == 15 @@ -53,10 +60,11 @@ def test_prefix(testonto: "Ontology", emmo: "Ontology") -> None: testonto.get_by_label(1) -def test_prefix_emmo(emmo: "Ontology") -> None: +def test_prefix_emmo() -> None: """Test prefix in ontology""" from emmopy import get_emmo + emmo = get_emmo() # Check that the prefix of emmo-inferred becomes emmo assert emmo.Atom.namespace.ontology.prefix == "emmo" assert emmo.get_by_label("Atom", prefix="emmo") == emmo.Atom