Skip to content

Commit 047f805

Browse files
author
Nicholas Car
authored
Merge pull request #1869 from gjhiggins/issue1868
resolve issue1868, add a method to expand qname to URI
2 parents 24d6070 + 2aa449c commit 047f805

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

rdflib/namespace/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,34 @@ def compute_qname_strict(
590590

591591
return self.__cache_strict[uri]
592592

593+
def expand_curie(self, curie: str) -> Union[URIRef, None]:
594+
"""
595+
Expand a CURIE of the form <prefix:element>, e.g. "rdf:type"
596+
into its full expression:
597+
598+
>>> import rdflib
599+
>>> g = rdflib.Graph()
600+
>>> g.namespace_manager.expand_curie("rdf:type")
601+
rdflib.term.URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')
602+
603+
Raises exception if a namespace is not bound to the prefix.
604+
605+
"""
606+
if not type(curie) is str:
607+
raise TypeError(f"Argument must be a string, not {type(curie).__name__}.")
608+
parts = curie.split(":", 1)
609+
if len(parts) != 2 or len(parts[0]) < 1:
610+
raise ValueError(
611+
"Malformed curie argument, format should be e.g. “foaf:name”."
612+
)
613+
ns = self.store.namespace(parts[0])
614+
if ns is not None:
615+
return URIRef(f"{str(ns)}{parts[1]}")
616+
else:
617+
raise ValueError(
618+
f"Prefix \"{curie.split(':')[0]}\" not bound to any namespace."
619+
)
620+
593621
def bind(
594622
self,
595623
prefix: Optional[str],

test/test_namespace/test_namespace.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import unittest
2+
from contextlib import ExitStack
3+
from multiprocessing.sharedctypes import Value
4+
from typing import Any, Optional, Type, Union
25
from unittest.case import expectedFailure
36
from warnings import warn
47

58
import pytest
69

710
from rdflib import DCTERMS
8-
from rdflib.graph import Graph
11+
from rdflib.graph import BNode, Graph, Literal
912
from rdflib.namespace import (
1013
FOAF,
1114
OWL,
@@ -260,3 +263,68 @@ def test_contains_method(self):
260263

261264
ref = URIRef("http://www.w3.org/2002/07/owl#real")
262265
assert ref in OWL, "OWL does not include owl:real"
266+
267+
def test_expand_curie_exception_messages(self) -> None:
268+
g = Graph()
269+
270+
with pytest.raises(TypeError) as e:
271+
assert g.namespace_manager.expand_curie(URIRef("urn:example")) == None
272+
assert str(e.value) == "Argument must be a string, not URIRef."
273+
274+
with pytest.raises(TypeError) as e:
275+
assert g.namespace_manager.expand_curie(Literal("rdf:type")) == None
276+
assert str(e.value) == "Argument must be a string, not Literal."
277+
278+
with pytest.raises(TypeError) as e:
279+
assert g.namespace_manager.expand_curie(BNode()) == None
280+
assert str(e.value) == "Argument must be a string, not BNode."
281+
282+
with pytest.raises(TypeError) as e:
283+
assert g.namespace_manager.expand_curie(Graph()) == None
284+
assert str(e.value) == "Argument must be a string, not Graph."
285+
286+
@pytest.mark.parametrize(
287+
["curie", "expected_result"],
288+
[
289+
("ex:tarek", URIRef("urn:example:tarek")),
290+
("ex:", URIRef(f"urn:example:")),
291+
("ex:a", URIRef(f"urn:example:a")),
292+
("ex:a:b", URIRef(f"urn:example:a:b")),
293+
("ex:a:b:c", URIRef(f"urn:example:a:b:c")),
294+
("ex", ValueError),
295+
("em:tarek", ValueError),
296+
("em:", ValueError),
297+
("em", ValueError),
298+
(":", ValueError),
299+
(":type", ValueError),
300+
("í", ValueError),
301+
(" :", ValueError),
302+
("", ValueError),
303+
("\n", ValueError),
304+
(None, TypeError),
305+
(3, TypeError),
306+
(URIRef("urn:example:"), TypeError),
307+
(BNode(), TypeError),
308+
(Literal("rdf:type"), TypeError),
309+
],
310+
)
311+
def test_expand_curie(
312+
self, curie: Any, expected_result: Union[Type[Exception], URIRef, None]
313+
) -> None:
314+
g = Graph(bind_namespaces="none")
315+
nsm = g.namespace_manager
316+
nsm.bind("ex", "urn:example:")
317+
result: Optional[URIRef] = None
318+
catcher: Optional[pytest.ExceptionInfo[Exception]] = None
319+
with ExitStack() as xstack:
320+
if isinstance(expected_result, type) and issubclass(
321+
expected_result, Exception
322+
):
323+
catcher = xstack.enter_context(pytest.raises(expected_result))
324+
result = g.namespace_manager.expand_curie(curie)
325+
326+
if catcher is not None:
327+
assert result is None
328+
assert catcher.value is not None
329+
else:
330+
assert expected_result == result

0 commit comments

Comments
 (0)