Skip to content

Commit

Permalink
fix: Raise ValueError for unsupported bind_namespace values
Browse files Browse the repository at this point in the history
This patch changes `NamespaceManager.__init__` to raise a `ValueError`
when it receives an unsupported value for `bind_namespaces`.

Other changes:
- Added a Literal type for valid `bind_namespaces` values, with this
  type checkers will raise an error if an invalid string is supplied as
  an argument.
- Rename some variables/properties to have a underscore in front to make
  it clear they are not part of the RDFLib public API.
  • Loading branch information
aucampia committed Apr 19, 2022
1 parent e30e386 commit 6e8c542
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 15 deletions.
10 changes: 7 additions & 3 deletions rdflib/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from io import BytesIO
from typing import (
IO,
TYPE_CHECKING,
Any,
BinaryIO,
Generator,
Expand Down Expand Up @@ -38,6 +39,9 @@
assert Literal # avoid warning
assert Namespace # avoid warning

if TYPE_CHECKING:
from rdflib.namespace import _NamespaceSetString

logger = logging.getLogger(__name__)

__doc__ = """\
Expand Down Expand Up @@ -329,7 +333,7 @@ def __init__(
identifier: Optional[Union[IdentifiedNode, str]] = None,
namespace_manager: Optional[NamespaceManager] = None,
base: Optional[str] = None,
bind_namespaces: str = "core",
bind_namespaces: "_NamespaceSetString" = "core",
):
super(Graph, self).__init__()
self.base = base
Expand All @@ -344,7 +348,7 @@ def __init__(
else:
self.__store = store
self.__namespace_manager = namespace_manager
self.bind_namespaces = bind_namespaces
self._bind_namespaces = bind_namespaces
self.context_aware = False
self.formula_aware = False
self.default_union = False
Expand All @@ -363,7 +367,7 @@ def namespace_manager(self):
this graph's namespace-manager
"""
if self.__namespace_manager is None:
self.__namespace_manager = NamespaceManager(self, self.bind_namespaces)
self.__namespace_manager = NamespaceManager(self, self._bind_namespaces)
return self.__namespace_manager

@namespace_manager.setter
Expand Down
25 changes: 18 additions & 7 deletions rdflib/namespace/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import sys
import warnings
from functools import lru_cache
from pathlib import Path
Expand Down Expand Up @@ -342,6 +343,14 @@ def _ipython_key_completions_(self) -> List[str]:

XMLNS = Namespace("http://www.w3.org/XML/1998/namespace")

if TYPE_CHECKING:
if sys.version_info >= (3, 8):
from typing import Literal as PyLiteral
else:
from typing_extensions import Literal as PyLiteral

_NamespaceSetString = PyLiteral["core", "rdflib", "none"]


class NamespaceManager(object):
"""Class for managing prefix => namespace mappings
Expand Down Expand Up @@ -386,7 +395,7 @@ class NamespaceManager(object):
"""

def __init__(self, graph: "Graph", bind_namespaces: str = "core"):
def __init__(self, graph: "Graph", bind_namespaces: "_NamespaceSetString" = "core"):
self.graph = graph
self.__cache: Dict[str, Tuple[str, URIRef, str]] = {}
self.__cache_strict: Dict[str, Tuple[str, URIRef, str]] = {}
Expand All @@ -405,21 +414,23 @@ def __init__(self, graph: "Graph", bind_namespaces: str = "core"):
pass
elif bind_namespaces == "rdflib":
# bind all the Namespaces shipped with RDFLib
for prefix, ns in NAMESPACE_PREFIXES_RDFLIB.items():
for prefix, ns in _NAMESPACE_PREFIXES_RDFLIB.items():
self.bind(prefix, ns)
# ... don't forget the core ones too
for prefix, ns in NAMESPACE_PREFIXES_CORE.items():
for prefix, ns in _NAMESPACE_PREFIXES_CORE.items():
self.bind(prefix, ns)
elif bind_namespaces == "cc":
# bind any prefix that can be found with lookups to prefix.cc
# first bind core and rdflib ones
# work out remainder - namespaces without prefixes
# only look those ones up
raise NotImplementedError("Haven't got to this option yet")
else: # bind_namespaces == "core":
elif bind_namespaces == "core":
# bind a few core RDF namespaces - default
for prefix, ns in NAMESPACE_PREFIXES_CORE.items():
for prefix, ns in _NAMESPACE_PREFIXES_CORE.items():
self.bind(prefix, ns)
else:
raise ValueError(f"unsupported namespace set {bind_namespaces}")

def __contains__(self, ref: str) -> bool:
# checks if a reference is in any of the managed namespaces with syntax
Expand Down Expand Up @@ -828,7 +839,7 @@ def get_longest_namespace(trie: Dict[str, Any], value: str) -> Optional[str]:
from rdflib.namespace._XSD import XSD

# prefixes for the core Namespaces shipped with RDFLib
NAMESPACE_PREFIXES_CORE = {
_NAMESPACE_PREFIXES_CORE = {
"owl": OWL,
"rdf": RDF,
"rdfs": RDFS,
Expand All @@ -839,7 +850,7 @@ def get_longest_namespace(trie: Dict[str, Any], value: str) -> Optional[str]:


# prefixes for all the non-core Namespaces shipped with RDFLib
NAMESPACE_PREFIXES_RDFLIB = {
_NAMESPACE_PREFIXES_RDFLIB = {
"brick": BRICK,
"csvw": CSVW,
"dc": DC,
Expand Down
94 changes: 89 additions & 5 deletions test/test_namespacemanager.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import logging
import sys
from contextlib import ExitStack
from pathlib import Path
from typing import Any, Dict, Optional, Set, Tuple, Type, Union

import pytest

from rdflib.graph import Dataset
from rdflib.term import URIRef

sys.path.append(str(Path(__file__).parent.parent.absolute()))
from rdflib import Graph
from rdflib.namespace import (
NAMESPACE_PREFIXES_CORE,
NAMESPACE_PREFIXES_RDFLIB,
_NAMESPACE_PREFIXES_CORE,
_NAMESPACE_PREFIXES_RDFLIB,
OWL,
RDFS,
NamespaceManager,
)


Expand All @@ -18,7 +25,7 @@ def test_core_prefixes_bound():
g = Graph()

# prefixes in Graph
assert len(list(g.namespaces())) == len(NAMESPACE_PREFIXES_CORE)
assert len(list(g.namespaces())) == len(_NAMESPACE_PREFIXES_CORE)
pre = sorted([x[0] for x in list(g.namespaces())])
assert pre == ["owl", "rdf", "rdfs", "xml", "xsd"]

Expand All @@ -27,8 +34,8 @@ def test_rdflib_prefixes_bound():
g = Graph(bind_namespaces="rdflib")

# the core 5 + the extra 23 namespaces with prefixes
assert len(list(g.namespaces())) == len(NAMESPACE_PREFIXES_CORE) + len(
list(NAMESPACE_PREFIXES_RDFLIB)
assert len(list(g.namespaces())) == len(_NAMESPACE_PREFIXES_CORE) + len(
list(_NAMESPACE_PREFIXES_RDFLIB)
)


Expand Down Expand Up @@ -78,3 +85,80 @@ def test_replace():
assert ("rdfs", URIRef("http://example.com")) in list(
g.namespace_manager.namespaces()
)


def test_invalid_selector() -> None:
graph = Graph()
with pytest.raises(ValueError):
NamespaceManager(graph, bind_namespaces="invalid") # type: ignore[arg-type]


NamespaceSet = Set[Tuple[str, URIRef]]


def check_graph_ns(
graph: Graph,
expected_nsmap: Dict[str, Any],
check_namespaces: Optional[NamespaceSet] = None,
) -> None:
expected_namespaces = {
(prefix, URIRef(f"{uri}")) for prefix, uri in expected_nsmap.items()
}
logging.debug("expected_namespaces = %s", expected_namespaces)
graph_namespaces = {*graph.namespaces()}
assert expected_namespaces == graph_namespaces
nman_namespaces = {*graph.namespace_manager.namespaces()}
assert expected_namespaces == nman_namespaces
if check_namespaces is not None:
assert expected_namespaces == check_namespaces
logging.debug("check_namespaces = %s", check_namespaces)


@pytest.mark.parametrize(
["selector", "expected_result"],
[
(None, ValueError),
("invalid", ValueError),
("core", _NAMESPACE_PREFIXES_CORE),
("rdflib", {**_NAMESPACE_PREFIXES_CORE, **_NAMESPACE_PREFIXES_RDFLIB}),
("none", {}),
],
)
def test_graph_bind_namespaces(
selector: Any,
expected_result: Union[Dict[str, Any], Type[Exception]],
) -> None:
namespaces: Optional[NamespaceSet] = None
with ExitStack() as xstack:
if not isinstance(expected_result, dict):
xstack.enter_context(pytest.raises(expected_result))
graph = Graph(bind_namespaces=selector)
namespaces = {*graph.namespaces()}
if isinstance(expected_result, dict):
assert namespaces is not None
check_graph_ns(graph, expected_result, namespaces)
else:
assert namespaces is None


@pytest.mark.parametrize(
["selector", "expected_result"],
[
(None, ValueError),
("invalid", ValueError),
("core", _NAMESPACE_PREFIXES_CORE),
("rdflib", {**_NAMESPACE_PREFIXES_CORE, **_NAMESPACE_PREFIXES_RDFLIB}),
("none", {}),
],
)
def test_nman_bind_namespaces(
selector: Any,
expected_result: Union[Dict[str, Any], Type[Exception]],
) -> None:
with ExitStack() as xstack:
if not isinstance(expected_result, dict):
xstack.enter_context(pytest.raises(expected_result))
graph = Dataset()
graph.namespace_manager = NamespaceManager(graph, selector)
if isinstance(expected_result, dict):
check_graph_ns(graph, expected_result)

0 comments on commit 6e8c542

Please sign in to comment.