Skip to content

Commit

Permalink
Elaborate a little more in the warning message for config.cache
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner committed Apr 17, 2024
1 parent af27188 commit 141f3ec
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 16 deletions.
29 changes: 14 additions & 15 deletions sphinx/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,30 +51,28 @@ class ConfigValue(NamedTuple):
rebuild: _ConfigRebuild


def is_serializable(obj: object, *, _recursive_guard: frozenset[int] = frozenset()) -> bool:
"""Check if object is serializable or not."""
def is_serializable(obj: object, *, _seen: frozenset[int] = frozenset()) -> bool:
"""Check if an object is serializable or not."""
if isinstance(obj, UNSERIALIZABLE_TYPES):
return False

# use id() to handle un-hashable objects
if id(obj) in _recursive_guard:
if id(obj) in _seen:
return True

if isinstance(obj, dict):
guard = _recursive_guard | {id(obj)}
for key, value in obj.items():
if (
not is_serializable(key, _recursive_guard=guard)
or not is_serializable(value, _recursive_guard=guard)
):
return False
seen = _seen | {id(obj)}
return all(
is_serializable(key, _seen=seen) and is_serializable(value, _seen=seen)
for key, value in obj.items()
)
elif isinstance(obj, (list, tuple, set, frozenset)):
guard = _recursive_guard | {id(obj)}
return all(is_serializable(item, _recursive_guard=guard) for item in obj)
seen = _seen | {id(obj)}
return all(is_serializable(item, _seen=seen) for item in obj)

# if an issue occurs for a non-serializable type, pickle will complain
# since the object is likely coming from a third-party extension (we
# natively expect 'simple' types and not weird ones)
# since the object is likely coming from a third-party extension
# (we natively expect 'simple' types and not weird ones)
return True


Expand Down Expand Up @@ -473,7 +471,8 @@ def __getstate__(self) -> dict:
# will always mark the config value as changed,
# and thus always invalidate the cache and perform a rebuild.
logger.warning(
__('cannot cache unpickable configuration value: %r'),
__('cannot cache unpickable configuration value: %r '
'(because it contains a function, class, or module object)'),
name,
type='config',
subtype='cache',
Expand Down
2 changes: 1 addition & 1 deletion tests/test_config/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def union(self, *args: Iterable[object]) -> UselessGuard:

# check that without recursive guards, a recursion error occurs
with pytest.raises(RecursionError):
assert is_serializable(subject, _recursive_guard=UselessGuard())
assert is_serializable(subject, _seen=UselessGuard())


def test_is_serializable() -> None:
Expand Down

0 comments on commit 141f3ec

Please sign in to comment.