object inspection: produce deterministic descriptions for nested collection datastructures (#11312)

``util.inspect.object_description`` already attempts to sort collections, but this can fail.
This commit handles the failure case by using string-based object descriptions
as a fallback deterministic sort ordering, and protects against recursive collections.

Co-authored-by: Chris Lamb <lamby@debian.org>
Co-authored-by: Faidon Liambotis <paravoid@debian.org>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
This commit is contained in:
James Addison
2023-07-27 22:45:51 +01:00
committed by GitHub
co-authored by Chris Lamb Faidon Liambotis Adam Turner
parent 7d16dc0cac
commit 467e94dc62
2 changed files with 108 additions and 28 deletions
+56 -2
View File
@@ -503,10 +503,32 @@ def test_set_sorting():
assert description == "{'a', 'b', 'c', 'd', 'e', 'f', 'g'}"
def test_set_sorting_enum():
class MyEnum(enum.Enum):
a = 1
b = 2
c = 3
set_ = set(MyEnum)
description = inspect.object_description(set_)
assert description == "{MyEnum.a, MyEnum.b, MyEnum.c}"
def test_set_sorting_fallback():
set_ = {None, 1}
description = inspect.object_description(set_)
assert description in ("{1, None}", "{None, 1}")
assert description == "{1, None}"
def test_deterministic_nested_collection_descriptions():
# sortable
assert inspect.object_description([{1, 2, 3, 10}]) == "[{1, 2, 3, 10}]"
assert inspect.object_description(({1, 2, 3, 10},)) == "({1, 2, 3, 10},)"
# non-sortable (elements of varying datatype)
assert inspect.object_description([{None, 1}]) == "[{1, None}]"
assert inspect.object_description(({None, 1},)) == "({1, None},)"
assert inspect.object_description([{None, 1, 'A'}]) == "[{'A', 1, None}]"
assert inspect.object_description(({None, 1, 'A'},)) == "({'A', 1, None},)"
def test_frozenset_sorting():
@@ -518,7 +540,39 @@ def test_frozenset_sorting():
def test_frozenset_sorting_fallback():
frozenset_ = frozenset((None, 1))
description = inspect.object_description(frozenset_)
assert description in ("frozenset({1, None})", "frozenset({None, 1})")
assert description == "frozenset({1, None})"
def test_nested_tuple_sorting():
tuple_ = ({"c", "b", "a"},) # nb. trailing comma
description = inspect.object_description(tuple_)
assert description == "({'a', 'b', 'c'},)"
tuple_ = ({"c", "b", "a"}, {"f", "e", "d"})
description = inspect.object_description(tuple_)
assert description == "({'a', 'b', 'c'}, {'d', 'e', 'f'})"
def test_recursive_collection_description():
dict_a_, dict_b_ = {"a": 1}, {"b": 2}
dict_a_["link"], dict_b_["link"] = dict_b_, dict_a_
description_a, description_b = (
inspect.object_description(dict_a_),
inspect.object_description(dict_b_),
)
assert description_a == "{'a': 1, 'link': {'b': 2, 'link': dict(...)}}"
assert description_b == "{'b': 2, 'link': {'a': 1, 'link': dict(...)}}"
list_c_, list_d_ = [1, 2, 3, 4], [5, 6, 7, 8]
list_c_.append(list_d_)
list_d_.append(list_c_)
description_c, description_d = (
inspect.object_description(list_c_),
inspect.object_description(list_d_),
)
assert description_c == "[1, 2, 3, 4, [5, 6, 7, 8, list(...)]]"
assert description_d == "[5, 6, 7, 8, [1, 2, 3, 4, list(...)]]"
def test_dict_customtype():