classification
Title: Provide args and kwargs attributes on mock call objects
Type: behavior Stage: patch review
Components: Library (Lib) Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: michael.foord Nosy List: cjw296, kakshay, kushal.das, michael.foord, remi.lapeyre, xtreak
Priority: normal Keywords: patch, patch, patch

Created on 2014-04-16 20:47 by michael.foord, last changed 2019-02-10 16:28 by xtreak.

Pull Requests
URL Status Linked Edit
PR 11807 open kakshay, 2019-02-10 09:14
PR 11807 open kakshay, 2019-02-10 09:14
PR 11807 open kakshay, 2019-02-10 09:14
Messages (8)
msg216585 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2014-04-16 20:47
The unittest.mock.call object could have args/kwargs attributes to easily access the arguments it was called with.
msg335136 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2019-02-09 18:13
args and kwargs property can be introduced on the call object to return the args and kwargs stored in the tuple. Currently wrapping call object with tuple can get a tuple of args and kwargs but a property would be helpful. A first attempt patch on this. Feedback on the API would be helpful.

$ ./python.exe
Python 3.8.0a1+ (heads/master:8a03ff2ff4, Feb  9 2019, 10:42:29)
[Clang 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from unittest.mock import Mock
>>> m = Mock()
>>> m(1, a=1)
<Mock name='mock()' id='4325199504'>
>>> m.call_args_list[0]
call(1, a=1)
>>> tuple(m.call_args_list[0]) # wrapping it with tuple can give args and kwargs currently
((1,), {'a': 1})
>>> m.call_args_list[0].args # With patch return args
(1,)
>>> m.call_args_list[0].kwargs # With patch return kwargs
{'a': 1}

A simple patch would be as below : 

➜  cpython git:(master) ✗ git diff | cat
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index ef5c55d6a1..ef1aa1dcea 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -2124,6 +2124,24 @@ class _Call(tuple):
     def index(self, *args, **kwargs):
         return self.__getattr__('index')(*args, **kwargs)

+    @property
+    def args(self):
+        if len(self) == 2:
+            args, kwargs = self
+        else:
+            name, args, kwargs = self
+
+        return args
+
+    @property
+    def kwargs(self):
+        if len(self) == 2:
+            args, kwargs = self
+        else:
+            name, args, kwargs = self
+
+        return kwargs
+
     def __repr__(self):
         if not self._mock_from_kall:
             name = self._mock_name or 'call'
msg335140 - (view) Author: Kumar Akshay (kakshay) * Date: 2019-02-09 21:13
Hey @xtreak, if you're not working on this, can I submit the patch?
msg335144 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2019-02-10 07:39
@kakshay, I am not working on it so feel free to pick it up. I stumbled upon this while looking into mock issues and I just posted a patch to gather API feedback. I guess it would be helpful if someone confirms it would be a good addition so that there is less rework.

Thanks
msg335146 - (view) Author: Kumar Akshay (kakshay) * Date: 2019-02-10 09:18
Thanks @xtreak!
I've added a PR with the following API

➜  cpython git:(fix-issue-21269) ✗ ./python.exe
Python 3.8.0a0 (heads/fix-issue-21269-dirty:2433a2ab70, Feb 10 2019, 14:24:54)
[Clang 10.0.0 (clang-1000.10.44.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from unittest.mock import Mock
>>> m = Mock()
>>> m(1, a=23)
<Mock name='mock()' id='4552188368'>
>>> m.call_args
call(1, a=23)
>>> m.call_args.args     #after this patch
(1,)
>>> m.call_args.kwargs   #after this patch
{'a': 23}
>>> m.call_args_list[0].args    #after this patch
(1,)
>>> m.call_args_list[0].kwargs   #after this patch
{'a': 23}
msg335160 - (view) Author: Rémi Lapeyre (remi.lapeyre) * Date: 2019-02-10 16:00
I like this patch, working with calls often feels weird and this change simplify attribute access.
msg335161 - (view) Author: Rémi Lapeyre (remi.lapeyre) * Date: 2019-02-10 16:08
@xtreak, couldn't we have made `_Call` inherit from namedtuple to achieve a similar result (albeit the handling of name would be weird)?
msg335162 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2019-02-10 16:28
This feels safer to me with respect to backwards compatibility and also that it might be easier to backport this to mock on GitHub which works with Python 2.7. I have less knowledge on difference between tuple and namedtuple internals so I might be wrong here.
History
Date User Action Args
2019-02-10 16:28:49xtreaksetkeywords: patch, patch, patch

messages: + msg335162
2019-02-10 16:08:20remi.lapeyresetmessages: + msg335161
2019-02-10 16:00:49remi.lapeyresetnosy: + remi.lapeyre
messages: + msg335160
2019-02-10 09:18:05kakshaysetmessages: + msg335146
2019-02-10 09:14:18kakshaysetkeywords: + patch
stage: needs patch -> patch review
pull_requests: + pull_request11821
2019-02-10 09:14:09kakshaysetkeywords: + patch
stage: needs patch -> needs patch
pull_requests: + pull_request11820
2019-02-10 09:14:00kakshaysetkeywords: + patch
stage: needs patch -> needs patch
pull_requests: + pull_request11819
2019-02-10 07:39:49xtreaksetmessages: + msg335144
2019-02-09 21:13:52kakshaysetnosy: + kakshay
messages: + msg335140
2019-02-09 18:13:06xtreaksetnosy: + xtreak, cjw296

messages: + msg335136
versions: + Python 3.8, - Python 3.5
2014-04-16 20:48:12michael.foordsetnosy: + kushal.das
versions: + Python 3.5
assignee: michael.foord
components: + Library (Lib)
type: behavior
stage: needs patch
2014-04-16 20:47:42michael.foordcreate