Skip to content

Commit 874fb69

Browse files
committed
bpo-37555: Fix _CallList and _Call order sensitivity
_Call and _CallList depend on ordering to correctly process that an object being compared to ANY with __eq__ should return True. This fix updates the comparison to check both a == b and b == a and return True if either condition is met, fixing situations from the tests in the previous two commits where assertEqual would not be commutative if checking _Call or _CallList objects. This seems like a reasonable fix considering that the Python data model specifies that if an object doesn't know how to compare itself to another object it should return NotImplemented, and that on getting NotImplemented from a == b, it should try b == a, implying that good behavior for __eq__ is commutative. This also flips the order of comparison in _CallList's __contains__ method, guaranteeing ANY will be on the left and have it's __eq__ called for equality checking, fixing the interaction between assert_has_calls and ANY. Co-author: Neal Finne <[email protected]>
1 parent 49c5310 commit 874fb69

File tree

1 file changed

+13
-4
lines changed

1 file changed

+13
-4
lines changed

Lib/unittest/mock.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,19 @@ def __contains__(self, value):
337337

338338
for i in range(0, len_self - len_value + 1):
339339
sub_list = self[i:i+len_value]
340-
if sub_list == value:
340+
if value == sub_list:
341341
return True
342342
return False
343343

344344
def __repr__(self):
345345
return pprint.pformat(list(self))
346346

347+
def __eq__(self, other):
348+
self_list = list(self)
349+
other_list = list(other)
350+
# checking equality both directions is necessary for ANY to work
351+
return self_list.__eq__(other_list) or other_list.__eq__(self_list)
352+
347353

348354
def _check_and_set_parent(parent, value, name, new_name):
349355
# function passed to create_autospec will have mock
@@ -2293,7 +2299,6 @@ def _format_call_signature(name, args, kwargs):
22932299
return message % formatted_args
22942300

22952301

2296-
22972302
class _Call(tuple):
22982303
"""
22992304
A tuple for holding the results of a call to a mock, either in the form
@@ -2403,8 +2408,12 @@ def __eq__(self, other):
24032408
if self_name and other_name != self_name:
24042409
return False
24052410

2406-
# this order is important for ANY to work!
2407-
return (other_args, other_kwargs) == (self_args, self_kwargs)
2411+
self_params = self_args, self_kwargs
2412+
other_params = other_args, other_kwargs
2413+
return (
2414+
self_params.__eq__(other_params)
2415+
or other_params.__eq__(self_params)
2416+
)
24082417

24092418

24102419
__ne__ = object.__ne__

0 commit comments

Comments
 (0)