Ensure frozenset object descriptions are reproducible

Whilst working on the Reproducible Builds effort [0], we noticed
that sphinx could generate output that is not reproducible.

In particular, the rendering of `frozenset` objects in default
arguments and elsewhere is currently non-determinstic.

For example:

    frozenset(['a', 'b', 'c'])

Might be rendered as any of:

    frozenset({'a', 'b', 'c'})
    frozenset({'a', 'c', 'b'})
    frozenset({'b', 'a', 'c'})
    frozenset({'b', 'c', 'a'})
    frozenset({'c', 'a', 'b'})
    frozenset({'c', 'b', 'a'})

Patch attached that sorts the contents of frozensets whilst rendering.
This is parallel to the `dict` and `set` type logic

  [0] https://reproducible-builds.org/
This commit is contained in:
Chris Lamb 2018-09-05 12:21:12 +01:00
parent daac35cda7
commit 17d32d625b
2 changed files with 27 additions and 0 deletions

View File

@ -278,6 +278,15 @@ def object_description(object):
template = "{%s}" if PY3 else "set([%s])"
return template % ", ".join(object_description(x)
for x in sorted_values)
if isinstance(object, frozenset):
try:
sorted_values = sorted(object)
except TypeError:
pass # Cannot sort frozenset values, fall back to generic repr
else:
template = "frozenset({%s})" if PY3 else "frozenset([%s])"
return template % ", ".join(object_description(x)
for x in sorted_values)
try:
s = repr(object)
except Exception:

View File

@ -391,6 +391,24 @@ def test_set_sorting_fallback():
assert description in ("set([1, None])", "set([None, 1])")
def test_frozenset_sorting():
frozenset_ = frozenset("gfedcba")
description = inspect.object_description(frozenset_)
if PY3:
assert description == "frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'})"
else:
assert description == "frozenset(['a', 'b', 'c', 'd', 'e', 'f', 'g'])"
def test_frozenset_sorting_fallback():
frozenset_ = frozenset((None, 1))
description = inspect.object_description(frozenset_)
if PY3:
assert description in ("frozenset({1, None})", "frozenset({None, 1})")
else:
assert description in ("frozenset([1, None])", "frozenset([None, 1])")
def test_dict_customtype():
class CustomType(object):
def __init__(self, value):