Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
bpo-37555: Update _CallList.__contains__ to respect ANY#14700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
94ddf54b4c7d78ad99a9d49c5310874fb69d72d6f5f0e8411f295eac18e964b883841af4844c784489c8344ef17bdf430dd3522b1f47699d38650c924973c0001d70825dec66File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -814,7 +814,8 @@ def _call_matcher(self, _call): | ||||||||||
| else: | ||||||||||
| name, args, kwargs = _call | ||||||||||
| try: | ||||||||||
| return name, sig.bind(*args, **kwargs) | ||||||||||
| bound_call = sig.bind(*args, **kwargs) | ||||||||||
| return call(name, bound_call.args, bound_call.kwargs) | ||||||||||
| except TypeError as e: | ||||||||||
| return e.with_traceback(None) | ||||||||||
| else: | ||||||||||
| @@ -863,9 +864,9 @@ def assert_called_with(self, /, *args, **kwargs): | ||||||||||
| def _error_message(): | ||||||||||
| msg = self._format_mock_failure_message(args, kwargs) | ||||||||||
| return msg | ||||||||||
| expected = self._call_matcher((args, kwargs)) | ||||||||||
| expected = self._call_matcher(_Call((args, kwargs), two=True)) | ||||||||||
| actual = self._call_matcher(self.call_args) | ||||||||||
| if expected != actual: | ||||||||||
| if actual != expected: | ||||||||||
| cause = expected if isinstance(expected, Exception) else None | ||||||||||
| raise AssertionError(_error_message()) from cause | ||||||||||
| @@ -925,10 +926,10 @@ def assert_any_call(self, /, *args, **kwargs): | ||||||||||
| The assert passes if the mock has *ever* been called, unlike | ||||||||||
| `assert_called_with` and `assert_called_once_with` that only pass if | ||||||||||
| the call is the most recent one.""" | ||||||||||
| expected = self._call_matcher((args, kwargs)) | ||||||||||
| expected = self._call_matcher(_Call((args, kwargs), two=True)) | ||||||||||
| cause = expected if isinstance(expected, Exception) else None | ||||||||||
| actual = [self._call_matcher(c) for c in self.call_args_list] | ||||||||||
| if expected not in actual: | ||||||||||
| cause = expected if isinstance(expected, Exception) else None | ||||||||||
| if cause or expected not in _AnyComparer(actual): | ||||||||||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think as per my commit we can remove Line 340 in 455122a
__contains__ does an equal per item and hence it does the same check at _Call.__eq__Line 2394 in 455122a
There are also no test failures and I see the behavior around comparison to be the same. We also can skip the check for cause as I go with the approach. @ElizabethU Thoughts?
| ||||||||||
| def__eq__(self, other): |
self is call(<ANY>, 1) and other is an item from the list, meaning that when it hits the reversed comparison at the end of the methodLine 2444 in 455122a
| return (other_args, other_kwargs) == (self_args, self_kwargs) |
ANY is on the right, and it doesn't work.At least that's what I'm seeing on my machine. I'd be really excited if you've found a way around _AnyComparer, but it seems like we still need it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any failures on checking out your branch and cherry picking my changes on to your branch. My terminal output as below assuming I am doing it correct.
➜ cpython git:(pr_14700_re) git pr 14700
From github.com:python/cpython
* [new ref] refs/pull/14700/head -> pr_14700
➜ cpython git:(pr_14700_re) git checkout pr_14700
Updating files: 100% (506/506), done.
Switched to branch 'pr_14700'
➜ cpython git:(pr_14700) git cherry-pick bc099891dd327e00bc0271da9ddb752ae9198ce6
[pr_14700 5b7c21b188] Remove AnyCompare and use call objects everywhere.
Date: Mon Aug 19 15:11:01 2019 +0530
2 files changed, 8 insertions(+), 21 deletions(-)
➜ cpython git:(pr_14700) ./python.exe -m unittest Lib.unittest.test.testmock
...........................................................................................................................................................................................................................................................................................................................................................................................................................................................
----------------------------------------------------------------------
Ran 443 tests in 1.470s
OK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also don't have any changes more to push too as I can see.
➜ cpython git:(pr_14700_re) git show --summary | cat
commit bc099891dd327e00bc0271da9ddb752ae9198ce6
Author: Xtreak <tir.karthi@gmail.com>
Date: Mon Aug 19 15:11:01 2019 +0530
Remove AnyCompare and use call objects everywhere.
➜ cpython git:(pr_14700_re) ./python.exe -m unittest Lib.unittest.test.testmock
.............................................................................................................................................................................................................................................................................................................................................................................................................................................................
----------------------------------------------------------------------
Ran 445 tests in 1.449s
OK
➜ cpython git:(pr_14700_re) git push origin pr_14700_re
Everything up-to-date
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self is call(, 1) and other is an item from the list, meaning that when it hits the reversed comparison at the end of the method
ANY is on the right, and it doesn't work.
It's a list and list.__contains__ is used only when it's not a list. So it takes the other code path sub_list == value to where value is the tuple that contains ANY. ANY is on the left as other_args and other_kwargs. as I can see.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed that this change in the order of comparison to be crucial to my patch over change in the arguments. That means we can either
- Have ANYCompare and apply this fix on 3.9 and 3.8 . I am not sure of backporting this to 3.7 at this point since it's in 3.7.5 but I will leave it upto the core dev.
- Depend on the below order in list comparison and remove ANYCompare to use just contains and keep the fix only to 3.9 since the listobject patch is not on 3.8 and below.
Thoughts?
diff --git a/Objects/listobject.c b/Objects/listobject.c
index d012ab933a..cea9b24a3b 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -449,8 +449,7 @@ list_contains(PyListObject *a, PyObject *el)
int cmp;
for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i)
- cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i),
- Py_EQ);
+ cmp = PyObject_RichCompareBool(PyList_GET_ITEM(a, i), el, Py_EQ);
return cmp;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking a bit further there is also mock backport which would need to run on 3.6+ so this would cause problems. Now I am more leaning towards to reverting my change to keep ANYCompare though the interpreter is fixed in 3.9+. At this point I think I depended on the patch in 3.9 during debugging and made some incorrect assumptions with respect to ordering, sorry about that. I would be okay with this landing on 3.8+ with ANYcompare and the backport can work on 3.6+ with the PR.
@ElizabethU Feel free to revert my patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am able to get it to work by rebasing 🎉 🎊 🎺 Thanks, I should have tried that earlier, but I thought since I was still having the problem on your branch and you had that passing, that it couldn't be a rebase. I should have tried anyway.
My input on the patch for 3.9 is that it's great and a big improvement over what I had alone and I would love to see it get reviewed whenever there's time. As for backporting, maybe we'd still need the _AnyComparer class for 3.8 then? Or maybe this just doesn't get backported? Not sure what's the norm, but I'm happy to learn and go along with it.
Thank you so, so much for sticking with me through this. I've learned so much, and your expertise has been a big part of that. I hope I can contribute again and take up a lot less of your time now that I know the routine and some of the codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tirkarthi Ah, I didn't see your last comments. So reverting is the right thing to do? I do like the patch, and was hoping we could keep it for 3.9, but I'm unfamiliar with the mock backport situation, and why it means we couldn't use the patch in just 3.9?
I'm reverting the patch per your instructions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, anything that means things work on 3.6+ would be very much appreciated by me :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more case: Please do the check for cause to be exception if cause or expected not in _AnyComparer(actual): in assert_any_await too to catch the exception like you did in assert_any_call. This would cause below test to fail. I had a test in my patch for this case. Something like below test :
diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/unittest/test/testmock/testasync.py
index 1b1f9111d2..87f3cfc8fa 100644
--- a/Lib/unittest/test/testmock/testasync.py
+++ b/Lib/unittest/test/testmock/testasync.py
@@ -192,6 +192,10 @@ class AsyncAutospecTest(unittest.TestCase):
spec.assert_awaited_with(1, 2, c=3)
spec.assert_awaited()
+ with self.assertRaises(AssertionError):
+ spec.assert_any_await(e=1)
+
+
def test_patch_with_autospec(self):
async def test_async():
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @tirkarthi ! I also saw in your patch you added the two=True parameter to assert_called_with which strikes me as good for consistency, so I added that back in too.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Fix `NonCallableMock._call_matcher` returning tuple instead of `_Call` object | ||
| when `self._spec_signature` exists. Patch by Elizabeth Uselton |
Uh oh!
There was an error while loading. Please reload this page.