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

resolve issue1868, add a method to expand qname to URI #1869

Merged
merged 15 commits into from Apr 28, 2022
28 changes: 28 additions & 0 deletions rdflib/namespace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,34 @@ def compute_qname_strict(

return self.__cache_strict[uri]

def expand_curie(self, curie: str) -> Union[URIRef, None]:
"""
Expand a CURIE of the form <prefix:element>, e.g. "rdf:type"
into its full expression:

>>> import rdflib
>>> g = rdflib.Graph()
>>> g.namespace_manager.expand_curie("rdf:type")
rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')

Raises exception if a namespace is not bound to the prefix.

"""
if not type(curie) is str:
raise TypeError(f"Argument must be a string, not {type(curie).__name__}.")
parts = curie.split(":", 1)
if len(parts) != 2 or len(parts[0]) < 1:
raise ValueError(
"Malformed curie argument, format should be e.g. “foaf:name”."
)
ns = self.store.namespace(parts[0])
if ns is not None:
return URIRef(f"{str(ns)}{parts[1]}")
else:
raise ValueError(
f"Prefix \"{curie.split(':')[0]}\" not bound to any namespace."
)

def bind(
self,
prefix: Optional[str],
Expand Down
70 changes: 69 additions & 1 deletion test/test_namespace/test_namespace.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import unittest
from contextlib import ExitStack
from multiprocessing.sharedctypes import Value
from typing import Any, Optional, Type, Union
from unittest.case import expectedFailure
from warnings import warn

import pytest

from rdflib import DCTERMS
from rdflib.graph import Graph
from rdflib.graph import BNode, Graph, Literal
from rdflib.namespace import (
FOAF,
OWL,
Expand Down Expand Up @@ -260,3 +263,68 @@ def test_contains_method(self):

ref = URIRef("http://www.w3.org/2002/07/owl#real")
assert ref in OWL, "OWL does not include owl:real"

def test_expand_curie_exception_messages(self) -> None:
g = Graph()

with pytest.raises(TypeError) as e:
assert g.namespace_manager.expand_curie(URIRef("urn:example")) == None
assert str(e.value) == "Argument must be a string, not URIRef."

with pytest.raises(TypeError) as e:
assert g.namespace_manager.expand_curie(Literal("rdf:type")) == None
assert str(e.value) == "Argument must be a string, not Literal."

with pytest.raises(TypeError) as e:
assert g.namespace_manager.expand_curie(BNode()) == None
assert str(e.value) == "Argument must be a string, not BNode."

with pytest.raises(TypeError) as e:
assert g.namespace_manager.expand_curie(Graph()) == None
assert str(e.value) == "Argument must be a string, not Graph."

@pytest.mark.parametrize(
["curie", "expected_result"],
[
("ex:tarek", URIRef("urn:example:tarek")),
("ex:", URIRef(f"urn:example:")),
("ex:a", URIRef(f"urn:example:a")),
("ex:a:b", URIRef(f"urn:example:a:b")),
("ex:a:b:c", URIRef(f"urn:example:a:b:c")),
("ex", ValueError),
("em:tarek", ValueError),
("em:", ValueError),
("em", ValueError),
(":", ValueError),
(":type", ValueError),
("í", ValueError),
(" :", ValueError),
("", ValueError),
("\n", ValueError),
(None, TypeError),
(3, TypeError),
(URIRef("urn:example:"), TypeError),
(BNode(), TypeError),
(Literal("rdf:type"), TypeError),
],
)
def test_expand_curie(
self, curie: Any, expected_result: Union[Type[Exception], URIRef, None]
) -> None:
g = Graph(bind_namespaces="none")
nsm = g.namespace_manager
nsm.bind("ex", "urn:example:")
result: Optional[URIRef] = None
catcher: Optional[pytest.ExceptionInfo[Exception]] = None
with ExitStack() as xstack:
if isinstance(expected_result, type) and issubclass(
expected_result, Exception
):
catcher = xstack.enter_context(pytest.raises(expected_result))
result = g.namespace_manager.expand_curie(curie)

if catcher is not None:
assert result is None
assert catcher.value is not None
else:
assert expected_result == result