Skip to content

Commit

Permalink
fix: DefinedNamespace: always raise AttributeError for _partialmethod
Browse files Browse the repository at this point in the history
This patch changes `DefinedNamespace` to always raise `AttributeError` if
`_partialmethod` is not defined, even if
`DefinedNamespace._fail == False`. This because this property is
accessed by `inspect.signature` when building docs and if `AttributeError` is not
raised when this property is accessed it results in infinite recursion.

```
WARNING: error while formatting signature for rdflib.namespace.DefinedNamespace: Handler <function record_typehints at 0x7fbf2696dd40> for event 'autodoc-process-signature' threw an exception (exception: maximum recursion depth exceeded while calling a Python object)
```
  • Loading branch information
aucampia committed Apr 15, 2022
1 parent 57f993d commit 637e106
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 2 deletions.
17 changes: 15 additions & 2 deletions rdflib/namespace/__init__.py
Expand Up @@ -190,6 +190,18 @@ def __repr__(self) -> str:
return f"URIPattern({super().__repr__()})"


# _RESERVED_ATTRS are attributes for which AttributeError should always be
# raised if they are not defined.
_RESERVED_ATTRS = {
# `_partialmethod` is being accessed by `inspect.signature` (see
# https://github.com/python/cpython/blob/72965981d1128b3923dad5e850c8cff626ae4dc7/Lib/inspect.py#L2428-L2431)
# and if it does not return AttributeError inspect behaves
# incorrectly. This results in warnings in sphinx which are being
# treated as errors.
"_partialmethod",
}


class DefinedNamespaceMeta(type):
"""Utility metaclass for generating URIRefs with a common prefix."""

Expand All @@ -204,8 +216,9 @@ def __getitem__(cls, name: str, default=None) -> URIRef:
name = str(name)
if str(name).startswith("__"):
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:

if (cls._warn or cls._fail or name in _RESERVED_ATTRS) and name not in cls:
if cls._fail or name in _RESERVED_ATTRS:
raise AttributeError(f"term '{name}' not in namespace '{cls._NS}'")
else:
warnings.warn(
Expand Down
27 changes: 27 additions & 0 deletions test/test_namespace/test_definednamespace.py
@@ -0,0 +1,27 @@
import pytest

from rdflib import BRICK

NOFAIL_DEFINED_NAMESPACE = BRICK


def test_nofail_attr() -> None:
"""
Undefined attributes do not raise an exception if `DefinedNamespace._fail`
is `False`.
"""
assert not NOFAIL_DEFINED_NAMESPACE._fail
assert (
f"{NOFAIL_DEFINED_NAMESPACE}not_defined_in_namespace"
== f"{NOFAIL_DEFINED_NAMESPACE.not_defined_in_namespace}"
)


def test_nofail_partialmethod_attrerror() -> None:
"""
Accessing `_partialmethod` raises an exception even if
`DefinedNamespace._fail` is `False`.
"""
assert not NOFAIL_DEFINED_NAMESPACE._fail
with pytest.raises(AttributeError):
_ = NOFAIL_DEFINED_NAMESPACE._partialmethod

0 comments on commit 637e106

Please sign in to comment.