Let's say I define a helper method to monkeypatch a simple modification into the __call__
behavior of an existing object:
def and_print_on_call(instance):
class AndPrintOnCall(type(instance)):
def __call__(self, *args, **kwarg):
print('printed!')
return super().__call__(*args, **kwarg)
instance.__class__ = AndPrintOnCall
If I apply this to a typical class instance, it works as intended:
class Foo:
def __call__(self):
print('foo!')
foo = Foo()
and_print_on_call(foo)
foo() # Prints "printed!" then "foo!"
But if I apply it to a MagicMock
instance, it doesn't:
foo = unittest.mock.MagicMock()
foo.side_effect = lambda: print('foo!')
and_print_on_call(foo)
foo() # Prints only "foo!"
Why? What is MagicMock
doing special, that calling an instance of it apparently doesn't reference __class__.__call__
like it would for a typical class?
To provide some broader context: I'm attempting to create a deep-copying mock, in order to handle mutable arguments (similar to what's discussed here, though none of those provided solutions quite meet my particular needs). In the real implementation, the print('printed!')
statement would instead be the copy logic.
Mocks intercept __class__
assignment:
def __setattr__(self, name, value):
...
elif name == '__class__':
self._spec_class = value
return
so your instance.__class__ = AndPrintOnCall
doesn't actually work.