This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: unittest.mock does not wrap dunder methods (__getitem__ etc)
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Darragh Bailey, andrei.avk, anthonypjshaw, cjw296, lisroach, mariocj89, michael.foord, pconnell, r.david.murray, rbcollins, xtreak
Priority: normal Keywords: patch

Created on 2015-11-10 17:02 by Darragh Bailey, last changed 2022-04-11 14:58 by admin.

Files
File name Uploaded Description Edit
test-mock-wraps-dict.py Darragh Bailey, 2015-11-10 17:02
Pull Requests
URL Status Linked Edit
PR 16029 merged xtreak, 2019-09-12 11:23
PR 19734 merged xtreak, 2020-04-27 14:43
Messages (12)
msg254453 - (view) Author: Darragh Bailey (Darragh Bailey) Date: 2015-11-10 17:02
Both unittest.mock and the backported release for earlier pythons don't appear to support mocking of dictionary objects.

Specifically I'm expecting that any of the methods used to test for membership, or get items from a mock object wrapping a dictionary should succeed. However it appears that MagicMock doesn't appear to support this.

Attached file shows an attempt to use different methods with a wrapped dictionary object where only the '.get()' method appears to work as expected.
msg254466 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-11-10 23:12
Looking at the source, it's not clear that wraps is supported for __ methods, despite what the documentation implies.  That is, MagicProxy doesn't seem to look at the wraps information.  I suspect it is doable, but it may be an enhancement request rather than a bug fix, I'm not sure.
msg261835 - (view) Author: Robert Collins (rbcollins) * (Python committer) Date: 2016-03-15 23:11
I think this is a valid mock bug; it likely needs some thoughtful exhaustive testing, and obviously support added for it.
msg341615 - (view) Author: anthony shaw (anthonypjshaw) * (Python triager) Date: 2019-05-06 19:29
The assertions in the attached test still fail on master (3.8a3), so this still applies. 

Michael, are you able to look at this, the code hasn't changed since the original PEP417 implementation, which doesn't specify if this behaviour should be supported.

The documentation does not specify that this is supported also, so i suspect this is an enhancement request.

        elif result is None:
            wraps = None
            if self._mock_wraps is not None:
                # XXXX should we get the attribute without triggering code
                # execution?
                wraps = getattr(self._mock_wraps, name)

            result = self._get_child_mock(
                parent=self, name=name, wraps=wraps, _new_name=name,
                _new_parent=self
            )
msg351469 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-09-09 14:03
This seems to a reasonable change to me since dict.get returns the value then making a contains check dict.__contains__ should return True instead of the fixed return value of False. Below is a patch where the mock_wraps attribute is set with the relevant method that would make sure in the report dict.get would used.


diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 298b41e0d7..077d22d08e 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -1935,6 +1935,12 @@ _side_effect_methods = {


 def _set_return_value(mock, method, name):
+    # If _mock_wraps is present then attach it so that it's return
+    # value is used when called.
+    if mock._mock_wraps is not None:
+        method._mock_wraps = getattr(mock._mock_wraps, name)
+        return
+
     fixed = _return_values.get(name, DEFAULT)
     if fixed is not DEFAULT:
         method.return_value = fixed
msg351824 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2019-09-11 11:15
Michael, any thoughts on this? This more feels like an enhancement to me and I have marked it as 3.9 since I am not sure someone might be depending on the behavior where they have used wraps but still expect default values for magicmethods as they do now.
msg352124 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2019-09-12 11:28
As discussed with Karthik, I think this is a nice feature enhancement for the wraps functionality and worth fixing. It has the great advantage that the fix is nice and isolated and simple.
msg352125 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2019-09-12 11:29
The previous behaviour was unspecified and clearly due to missing functionality, so the advantages of fixing it outweigh any potential compatibility issues. But I'd see it as a feature enhancement for 3.9.
msg360739 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2020-01-27 06:48
New changeset 72b1004657e60c900e4cd031b2635b587f4b280e by Chris Withers (Karthikeyan Singaravelan) in branch 'master':
bpo-25597: Ensure wraps' return value is used for magic methods in MagicMock (#16029)
https://github.com/python/cpython/commit/72b1004657e60c900e4cd031b2635b587f4b280e
msg367559 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2020-04-28 19:22
New changeset 521c8d6806adf0305c158d280ec00cca48e8ab22 by Karthikeyan Singaravelan in branch 'master':
bpo-39966: Revert "bpo-25597: Ensure wraps' return value is used for magic methods in MagicMock" (GH-19734)
https://github.com/python/cpython/commit/521c8d6806adf0305c158d280ec00cca48e8ab22
msg367623 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python committer) Date: 2020-04-29 04:16
The change has been reverted as per issue39966. I am reopening this for further discussion.
msg400457 - (view) Author: Andrei Kulakov (andrei.avk) * (Python triager) Date: 2021-08-28 03:05
I went through dunder methods to check if any other operators or builtins work on objects without respective dunder methods:

 - del x works even though there is no object.__del__

 - operator.length_hint() => 0 when there is no object.__length_hint__

So in addition to __bool__, these dunder methods would have to be special cased.
History
Date User Action Args
2022-04-11 14:58:23adminsetgithub: 69783
2021-08-28 03:05:05andrei.avksetnosy: + andrei.avk
messages: + msg400457
2020-04-29 04:16:28xtreaksetstatus: closed -> open
resolution: fixed ->
messages: + msg367623

stage: resolved ->
2020-04-28 19:22:39cjw296setmessages: + msg367559
2020-04-27 14:43:50xtreaksetpull_requests: + pull_request19056
2020-01-29 03:27:46xtreaksetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2020-01-29 03:23:22josephgordonsetnosy: - josephgordon
2020-01-27 06:48:29cjw296setmessages: + msg360739
2019-10-28 20:48:53pconnellsetnosy: + pconnell
2019-09-12 11:29:41michael.foordsetmessages: + msg352125
2019-09-12 11:28:18michael.foordsetmessages: + msg352124
2019-09-12 11:23:57xtreaksetkeywords: + patch
stage: test needed -> patch review
pull_requests: + pull_request15652
2019-09-11 11:15:38xtreaksetversions: + Python 3.9, - Python 3.5
nosy: + lisroach

messages: + msg351824

components: + Library (Lib)
2019-09-09 14:03:32xtreaksetnosy: + cjw296, mariocj89
messages: + msg351469
2019-05-06 19:29:13anthonypjshawsetnosy: + anthonypjshaw
messages: + msg341615
2018-09-22 19:15:12xtreaksetnosy: + xtreak
2016-03-15 23:11:30rbcollinssettitle: unittest.mock does not wrap dict objects correctly -> unittest.mock does not wrap dunder methods (__getitem__ etc)
messages: + msg261835
stage: test needed
2015-12-22 08:31:35josephgordonsetnosy: + josephgordon
2015-11-10 23:12:01r.david.murraysetnosy: + r.david.murray, michael.foord, rbcollins
messages: + msg254466
2015-11-10 17:02:38Darragh Baileycreate