Skip to content

Commit

Permalink
Fix defined namespace warnings (#2964)
Browse files Browse the repository at this point in the history
* Fix defined namespace warnings

Current docs-generation tests are polluted by lots of warnings that occur when Sphinx tries to read various parts of DefinedNamespace.

* Fix tests that no longer need incorrect exceptions handled.

* fix black formatting in test file

* Undo typing changes, so this works on current pre-3.9 branch

* better handling for any/all double-underscore properties

* Don't include __slots__ in dir().
  • Loading branch information
ashleysommer authored and edmondchuc committed Jan 15, 2025
1 parent c62acb5 commit b8a5e39
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 34 deletions.
44 changes: 29 additions & 15 deletions rdflib/namespace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ def __repr__(self) -> str:
# considered part of __dir__ results. These should be all annotations on
# `DefinedNamespaceMeta`.
_DFNS_RESERVED_ATTRS: Set[str] = {
"__slots__",
"_NS",
"_warn",
"_fail",
Expand All @@ -244,6 +245,8 @@ def __repr__(self) -> str:
class DefinedNamespaceMeta(type):
"""Utility metaclass for generating URIRefs with a common prefix."""

__slots__: Tuple[str, ...] = tuple()

_NS: Namespace
_warn: bool = True
_fail: bool = False # True means mimic ClosedNamespace
Expand All @@ -255,15 +258,11 @@ def __getitem__(cls, name: str, default=None) -> URIRef:
name = str(name)

if name in _DFNS_RESERVED_ATTRS:
raise AttributeError(
f"DefinedNamespace like object has no attribute {name!r}"
raise KeyError(
f"DefinedNamespace like object has no access item named {name!r}"
)
elif name in _IGNORED_ATTR_LOOKUP:
raise KeyError()
if str(name).startswith("__"):
# NOTE on type ignore: This seems to be a real bug, super() does not
# implement this method, it will fail if it is ever reached.
return super().__getitem__(name, default) # type: ignore[misc] # undefined in superclass
if (cls._warn or cls._fail) and name not in cls:
if cls._fail:
raise AttributeError(f"term '{name}' not in namespace '{cls._NS}'")
Expand All @@ -277,26 +276,39 @@ def __getitem__(cls, name: str, default=None) -> URIRef:
def __getattr__(cls, name: str):
if name in _IGNORED_ATTR_LOOKUP:
raise AttributeError()
elif name in _DFNS_RESERVED_ATTRS:
raise AttributeError(
f"DefinedNamespace like object has no attribute {name!r}"
)
elif name.startswith("__"):
return super(DefinedNamespaceMeta, cls).__getattribute__(name)
return cls.__getitem__(name)

def __repr__(cls) -> str:
return f"Namespace({str(cls._NS)!r})"
try:
ns_repr = repr(cls._NS)
except AttributeError:
ns_repr = "<DefinedNamespace>"
return f"Namespace({ns_repr})"

def __str__(cls) -> str:
return str(cls._NS)
try:
return str(cls._NS)
except AttributeError:
return "<DefinedNamespace>"

def __add__(cls, other: str) -> URIRef:
return cls.__getitem__(other)

def __contains__(cls, item: str) -> bool:
"""Determine whether a URI or an individual item belongs to this namespace"""
try:
this_ns = cls._NS
except AttributeError:
return False
item_str = str(item)
if item_str.startswith("__"):
# NOTE on type ignore: This seems to be a real bug, super() does not
# implement this method, it will fail if it is ever reached.
return super().__contains__(item) # type: ignore[misc] # undefined in superclass
if item_str.startswith(str(cls._NS)):
item_str = item_str[len(str(cls._NS)) :]
if item_str.startswith(str(this_ns)):
item_str = item_str[len(str(this_ns)) :]
return any(
item_str in c.__annotations__
or item_str in c._extras
Expand All @@ -313,7 +325,7 @@ def __dir__(cls) -> Iterable[str]:
return values

def as_jsonld_context(self, pfx: str) -> dict: # noqa: N804
"""Returns this DefinedNamespace as a a JSON-LD 'context' object"""
"""Returns this DefinedNamespace as a JSON-LD 'context' object"""
terms = {pfx: str(self._NS)}
for key, term in self.__annotations__.items():
if issubclass(term, URIRef):
Expand All @@ -328,6 +340,8 @@ class DefinedNamespace(metaclass=DefinedNamespaceMeta):
Warnings are emitted if unknown members are referenced if _warn is True
"""

__slots__: Tuple[str, ...] = tuple()

def __init__(self):
raise TypeError("namespace may not be instantiated")

Expand Down
28 changes: 9 additions & 19 deletions test/test_namespace/test_definednamespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,9 @@ def test_repr(dfns: Type[DefinedNamespace]) -> None:
ns_uri = f"{prefix}{dfns_info.suffix}"
logging.debug("ns_uri = %s", ns_uri)

repr_str: Optional[str] = None

with ExitStack() as xstack:
if dfns_info.suffix is None:
xstack.enter_context(pytest.raises(AttributeError))
repr_str = f"{dfns_info.dfns!r}"
repr_str: str = f"{dfns_info.dfns!r}"
if dfns_info.suffix is None:
assert repr_str is None
assert "<DefinedNamespace>" in repr_str
else:
assert repr_str is not None
repro = eval(repr_str)
Expand Down Expand Up @@ -368,20 +363,15 @@ def test_contains(
dfns_info = get_dfns_info(dfns)
if dfns_info.suffix is not None:
logging.debug("dfns_info = %s", dfns_info)
if dfns_info.has_attrs is False:
if dfns_info.has_attrs is False or dfns_info.suffix is None:
is_defined = False
does_contain: Optional[bool] = None
with ExitStack() as xstack:
if dfns_info.suffix is None:
xstack.enter_context(pytest.raises(AttributeError))
does_contain = attr_name in dfns
if dfns_info.suffix is not None:
if is_defined:
assert does_contain is True
else:
assert does_contain is False

does_contain: bool = attr_name in dfns

if is_defined:
assert does_contain is True
else:
assert does_contain is None
assert does_contain is False


@pytest.mark.parametrize(
Expand Down

0 comments on commit b8a5e39

Please # to comment.