Skip to content

Commit 896c8b9

Browse files
fix: display output failure message in insertion order of dictionary
1 parent e920a27 commit 896c8b9

File tree

3 files changed

+93
-14
lines changed

3 files changed

+93
-14
lines changed

src/_pytest/_io/pprint.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def __init__(
6262
indent: int = 4,
6363
width: int = 80,
6464
depth: int | None = None,
65+
sort_dicts: bool = True,
6566
) -> None:
6667
"""Handle pretty printing operations onto a stream using a set of
6768
configured parameters.
@@ -74,6 +75,9 @@ def __init__(
7475
7576
depth
7677
The maximum depth to print out nested structures.
78+
79+
sort_dicts
80+
If true, dict keys are sorted.
7781
7882
"""
7983
if indent < 0:
@@ -85,6 +89,7 @@ def __init__(
8589
self._depth = depth
8690
self._indent_per_level = indent
8791
self._width = width
92+
self._sort_dicts = sort_dicts
8893

8994
def pformat(self, object: Any) -> str:
9095
sio = _StringIO()
@@ -162,7 +167,10 @@ def _pprint_dict(
162167
) -> None:
163168
write = stream.write
164169
write("{")
165-
items = sorted(object.items(), key=_safe_tuple)
170+
if self._sort_dicts:
171+
items = sorted(object.items(), key=_safe_tuple)
172+
else:
173+
items = object.items()
166174
self._format_dict_items(items, stream, indent, allowance, context, level)
167175
write("}")
168176

@@ -608,7 +616,11 @@ def _safe_repr(
608616
components: list[str] = []
609617
append = components.append
610618
level += 1
611-
for k, v in sorted(object.items(), key=_safe_tuple):
619+
if self._sort_dicts:
620+
items = sorted(object.items(), key=_safe_tuple)
621+
else:
622+
items = object.items()
623+
for k, v in items:
612624
krepr = self._safe_repr(k, context, maxlevels, level)
613625
vrepr = self._safe_repr(v, context, maxlevels, level)
614626
append(f"{krepr}: {vrepr}")

src/_pytest/assertion/util.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,8 @@ def _compare_eq_iterable(
348348
# dynamic import to speedup pytest
349349
import difflib
350350

351-
left_formatting = PrettyPrinter().pformat(left).splitlines()
352-
right_formatting = PrettyPrinter().pformat(right).splitlines()
351+
left_formatting = PrettyPrinter(sort_dicts=False).pformat(left).splitlines()
352+
right_formatting = PrettyPrinter(sort_dicts=False).pformat(right).splitlines()
353353

354354
explanation = ["", "Full diff:"]
355355
# "right" is the expected base against which we compare "left",
@@ -505,29 +505,32 @@ def _compare_eq_dict(
505505
set_left = set(left)
506506
set_right = set(right)
507507
common = set_left.intersection(set_right)
508-
same = {k: left[k] for k in common if left[k] == right[k]}
508+
same = {k: left[k] for k in left if k in right and left[k] == right[k]}
509509
if same and verbose < 2:
510510
explanation += [f"Omitting {len(same)} identical items, use -vv to show"]
511511
elif same:
512512
explanation += ["Common items:"]
513-
explanation += highlighter(pprint.pformat(same)).splitlines()
513+
# Common items are displayed in the order of the left dict
514+
explanation += highlighter(pprint.pformat(same, sort_dicts=False)).splitlines()
514515
diff = {k for k in common if left[k] != right[k]}
515516
if diff:
516517
explanation += ["Differing items:"]
517-
for k in diff:
518-
explanation += [
519-
highlighter(saferepr({k: left[k]}))
520-
+ " != "
521-
+ highlighter(saferepr({k: right[k]}))
522-
]
518+
# Differing items are displayed in the order of the left dict
519+
for k in left:
520+
if k in diff:
521+
explanation += [
522+
highlighter(saferepr({k: left[k]}))
523+
+ " != "
524+
+ highlighter(saferepr({k: right[k]}))
525+
]
523526
extra_left = set_left - set_right
524527
len_extra_left = len(extra_left)
525528
if len_extra_left:
526529
explanation.append(
527530
f"Left contains {len_extra_left} more item{'' if len_extra_left == 1 else 's'}:"
528531
)
529532
explanation.extend(
530-
highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines()
533+
highlighter(pprint.pformat({k: left[k] for k in left if k in extra_left}, sort_dicts=False)).splitlines()
531534
)
532535
extra_right = set_right - set_left
533536
len_extra_right = len(extra_right)
@@ -536,7 +539,7 @@ def _compare_eq_dict(
536539
f"Right contains {len_extra_right} more item{'' if len_extra_right == 1 else 's'}:"
537540
)
538541
explanation.extend(
539-
highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines()
542+
highlighter(pprint.pformat({k: right[k] for k in right if k in extra_right}, sort_dicts=False)).splitlines()
540543
)
541544
return explanation
542545

testing/test_error_diffs.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,70 @@ def test_this():
198198
""",
199199
id="Compare dicts with differing items",
200200
),
201+
pytest.param(
202+
"""
203+
def test_this():
204+
result = {'d': 4, 'c': 3, 'b': 2, 'a': 1}
205+
expected = {'d': 4, 'c': 3, 'e': 5}
206+
assert result == expected
207+
""",
208+
"""
209+
> assert result == expected
210+
E AssertionError: assert {'d': 4, 'c': 3, 'b': 2, 'a': 1} == {'d': 4, 'c': 3, 'e': 5}
211+
E
212+
E Common items:
213+
E {'d': 4, 'c': 3}
214+
E Left contains 2 more items:
215+
E {'b': 2, 'a': 1}
216+
E Right contains 1 more item:
217+
E {'e': 5}
218+
E
219+
E Full diff:
220+
E {
221+
E 'd': 4,
222+
E 'c': 3,
223+
E - 'e': 5,
224+
E ? ^ ^
225+
E + 'b': 2,
226+
E ? ^ ^
227+
E + 'a': 1,
228+
E }
229+
""",
230+
id="Compare dicts and check order of diff",
231+
),
232+
pytest.param(
233+
"""
234+
def test_this():
235+
result = {'c': 3, 'd': 4, 'b': 2, 'a': 1}
236+
expected = {'d': 5, 'c': 3, 'b': 1}
237+
assert result == expected
238+
""",
239+
"""
240+
> assert result == expected
241+
E AssertionError: assert {'c': 3, 'd': 4, 'b': 2, 'a': 1} == {'d': 5, 'c': 3, 'b': 1}
242+
E
243+
E Common items:
244+
E {'c': 3}
245+
E Differing items:
246+
E {'d': 4} != {'d': 5}
247+
E {'b': 2} != {'b': 1}
248+
E Left contains 1 more item:
249+
E {'a': 1}
250+
E
251+
E Full diff:
252+
E {
253+
E - 'd': 5,
254+
E 'c': 3,
255+
E + 'd': 4,
256+
E - 'b': 1,
257+
E ? ^
258+
E + 'b': 2,
259+
E ? ^
260+
E + 'a': 1,
261+
E }
262+
""",
263+
id="Compare dicts with different order and values",
264+
),
201265
pytest.param(
202266
"""
203267
def test_this():

0 commit comments

Comments
 (0)