]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-119600: mock: do not access attributes of original when new_callable is set (...
authorRobert Collins <robert.collins@cognite.com>
Tue, 11 Jun 2024 05:41:12 +0000 (07:41 +0200)
committerGitHub <noreply@github.com>
Tue, 11 Jun 2024 05:41:12 +0000 (06:41 +0100)
In order to patch flask.g e.g. as in #84982, that
proxies getattr must not be invoked. For that,
mock must not try to read from the original
object. In some cases that is unavoidable, e.g.
when doing autospec. However, patch("flask.g",
new_callable=MagicMock) should be entirely safe.

Lib/test/test_unittest/testmock/support.py
Lib/test/test_unittest/testmock/testpatch.py
Lib/unittest/mock.py
Misc/NEWS.d/next/Library/2024-06-10-14-00-40.gh-issue-119600.jJMf4C.rst [new file with mode: 0644]

index 49986d65dc47af6fef681e9662a5ceee7b7696b7..6c535b7944f261e272da60afd0529472259b3e69 100644 (file)
@@ -14,3 +14,14 @@ class SomeClass(object):
 
 class X(object):
     pass
+
+# A standin for weurkzeug.local.LocalProxy - issue 119600
+def _inaccessible(*args, **kwargs):
+    raise AttributeError
+
+
+class OpaqueProxy:
+    __getattribute__ = _inaccessible
+
+
+g = OpaqueProxy()
index be75fda7826af174332e0856eb5cdd35ec2877ba..f26e74ce0bc1ba7f907c5cf585ec37aed6745457 100644 (file)
@@ -2045,6 +2045,13 @@ class PatchTest(unittest.TestCase):
         with self.assertRaises(TypeError):
             test()
 
+    def test_patch_proxy_object(self):
+        @patch("test.test_unittest.testmock.support.g", new_callable=MagicMock())
+        def test(_):
+            pass
+
+        test()
+
 
 if __name__ == '__main__':
     unittest.main()
index 3ef83e263f53b74184a3c66153f06e88cfea2830..edabb4520c13cdb3ab695bd1025f6ff1dd5ce2b1 100644 (file)
@@ -1508,13 +1508,12 @@ class _patch(object):
                 if isinstance(original, type):
                     # If we're patching out a class and there is a spec
                     inherit = True
-            if spec is None and _is_async_obj(original):
-                Klass = AsyncMock
-            else:
-                Klass = MagicMock
-            _kwargs = {}
+
+            # Determine the Klass to use
             if new_callable is not None:
                 Klass = new_callable
+            elif spec is None and _is_async_obj(original):
+                Klass = AsyncMock
             elif spec is not None or spec_set is not None:
                 this_spec = spec
                 if spec_set is not None:
@@ -1527,7 +1526,12 @@ class _patch(object):
                     Klass = AsyncMock
                 elif not_callable:
                     Klass = NonCallableMagicMock
+                else:
+                    Klass = MagicMock
+            else:
+                Klass = MagicMock
 
+            _kwargs = {}
             if spec is not None:
                 _kwargs['spec'] = spec
             if spec_set is not None:
diff --git a/Misc/NEWS.d/next/Library/2024-06-10-14-00-40.gh-issue-119600.jJMf4C.rst b/Misc/NEWS.d/next/Library/2024-06-10-14-00-40.gh-issue-119600.jJMf4C.rst
new file mode 100644 (file)
index 0000000..04c9ca9
--- /dev/null
@@ -0,0 +1,2 @@
+Fix :func:`unittest.mock.patch` to not read attributes of the target when
+``new_callable`` is set. Patch by Robert Collins.