Skip to content

Commit 959f73f

Browse files
committed
Improve _stable_hash()
1 parent 6de9e0c commit 959f73f

File tree

1 file changed

+15
-10
lines changed

1 file changed

+15
-10
lines changed

sphinx/builders/html/__init__.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import re
1111
import sys
1212
import time
13+
import types
1314
import warnings
1415
from os import path
1516
from typing import IO, TYPE_CHECKING, Any
@@ -75,16 +76,20 @@
7576
]
7677

7778

78-
def get_stable_hash(obj: Any) -> str:
79-
"""
80-
Return a stable hash for a Python data structure. We can't just use
81-
the md5 of str(obj) since for example dictionary items are enumerated
82-
in unpredictable order due to hash randomization in newer Pythons.
79+
def _stable_hash(obj: Any) -> str:
80+
"""Return a stable hash for a Python data structure.
81+
82+
We can't just use the md5 of str(obj) as the order of collections
83+
may be random.
8384
"""
8485
if isinstance(obj, dict):
85-
return get_stable_hash(list(obj.items()))
86-
elif isinstance(obj, (list, tuple)):
87-
obj = sorted(get_stable_hash(o) for o in obj)
86+
obj = sorted(map(_stable_hash, obj.items()))
87+
if isinstance(obj, (list, tuple, set, frozenset)):
88+
obj = sorted(map(_stable_hash, obj))
89+
elif isinstance(obj, (type, types.FunctionType)):
90+
# The default repr() of functions includes the ID, which is not ideal.
91+
# We use the fully qualified name instead.
92+
obj = f'{obj.__module__}.{obj.__qualname__}'
8893
return hashlib.md5(str(obj).encode(), usedforsecurity=False).hexdigest()
8994

9095

@@ -132,10 +137,10 @@ def __init__(
132137

133138
if config:
134139
values = {c.name: c.value for c in config.filter(config_categories)}
135-
self.config_hash = get_stable_hash(values)
140+
self.config_hash = _stable_hash(values)
136141

137142
if tags:
138-
self.tags_hash = get_stable_hash(sorted(tags))
143+
self.tags_hash = _stable_hash(sorted(tags))
139144

140145
def __eq__(self, other: BuildInfo) -> bool: # type: ignore[override]
141146
return (self.config_hash == other.config_hash and

0 commit comments

Comments
 (0)