]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-37555: Fix _CallList and _Call order sensitivity
authorElizabeth Uselton <elizabeth.uselton@rover.com>
Thu, 18 Jul 2019 06:28:50 +0000 (23:28 -0700)
committerElizabeth Uselton <elizabeth.uselton@rover.com>
Thu, 18 Jul 2019 06:28:50 +0000 (23:28 -0700)
_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>

Lib/unittest/mock.py

index c2802726d75d9c59db7fc1b264b1e1aec1182f09..93379dbfb56e684921737c8c85103f19b802d64a 100644 (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__