Skip to content

Commit

Permalink
pythongh-119600: mock: do not access attributes of original when new_…
Browse files Browse the repository at this point in the history
…callable is set (python#119601)

In order to patch flask.g e.g. as in python#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.
  • Loading branch information
rbtcollins authored and mrahtz committed Jun 30, 2024
1 parent c4115e9 commit 18a58be
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 18a58be

Please sign in to comment.