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 <neal@neal.finne.com>
This commit is contained in:
Elizabeth Uselton 2019-07-17 23:28:50 -07:00
parent 49c5310ad4
commit 874fb697b8
1 changed files with 13 additions and 4 deletions

View File

@ -337,13 +337,19 @@ class _CallList(list):
for i in range(0, len_self - len_value + 1):
sub_list = self[i:i+len_value]
if sub_list == value:
if value == sub_list:
return True
return False
def __repr__(self):
return pprint.pformat(list(self))
def __eq__(self, other):
self_list = list(self)
other_list = list(other)
# checking equality both directions is necessary for ANY to work
return self_list.__eq__(other_list) or other_list.__eq__(self_list)
def _check_and_set_parent(parent, value, name, new_name):
# function passed to create_autospec will have mock
@ -2293,7 +2299,6 @@ def _format_call_signature(name, args, kwargs):
return message % formatted_args
class _Call(tuple):
"""
A tuple for holding the results of a call to a mock, either in the form
@ -2403,8 +2408,12 @@ class _Call(tuple):
if self_name and other_name != self_name:
return False
# this order is important for ANY to work!
return (other_args, other_kwargs) == (self_args, self_kwargs)
self_params = self_args, self_kwargs
other_params = other_args, other_kwargs
return (
self_params.__eq__(other_params)
or other_params.__eq__(self_params)
)
__ne__ = object.__ne__