Skip to content

Commit

Permalink
[3.13] gh-119600: mock: do not access attributes of original when new…
Browse files Browse the repository at this point in the history
…_callable is set (GH-119601) (#120334)

gh-119600: mock: do not access attributes of original when new_callable is set (GH-119601)

In order to patch flask.g e.g. as in GH-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.
(cherry picked from commit 422c4fc)

Co-authored-by: Robert Collins <robert.collins@cognite.com>
  • Loading branch information
miss-islington and rbtcollins committed Jun 11, 2024
1 parent ffc8e21 commit aba5f2a
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 5 deletions.
11 changes: 11 additions & 0 deletions Lib/test/test_unittest/testmock/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,14 @@ def wibble(self): pass

class X(object):
pass

# A standin for weurkzeug.local.LocalProxy - issue 119600
def _inaccessible(*args, **kwargs):
raise AttributeError


class OpaqueProxy:
__getattribute__ = _inaccessible


g = OpaqueProxy()
7 changes: 7 additions & 0 deletions Lib/test/test_unittest/testmock/testpatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2045,6 +2045,13 @@ def test(): pass
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()
14 changes: 9 additions & 5 deletions Lib/unittest/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -1508,13 +1508,12 @@ def __enter__(self):
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:
Expand All @@ -1527,7 +1526,12 @@ def __enter__(self):
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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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.

0 comments on commit aba5f2a

Please sign in to comment.