Search code examples
pythontimemockingpython-mock

Patch __call__ of a function


I need to patch current datetime in tests. I am using this solution:

def _utcnow():
    return datetime.datetime.utcnow()


def utcnow():
    """A proxy which can be patched in tests.
    """
    # another level of indirection, because some modules import utcnow
    return _utcnow()

Then in my tests I do something like:

    with mock.patch('***.utils._utcnow', return_value=***):
        ...

But today an idea came to me, that I could make the implementation simpler by patching __call__ of function utcnow instead of having an additional _utcnow.

This does not work for me:

    from ***.utils import utcnow
    with mock.patch.object(utcnow, '__call__', return_value=***):
        ...

How to do this elegantly?


Solution

  • When you patch __call__ of a function, you are setting the __call__ attribute of that instance. Python actually calls the __call__ method defined on the class.

    For example:

    >>> class A(object):
    ...     def __call__(self):
    ...         print 'a'
    ...
    >>> a = A()
    >>> a()
    a
    >>> def b(): print 'b'
    ...
    >>> b()
    b
    >>> a.__call__ = b
    >>> a()
    a
    >>> a.__call__ = b.__call__
    >>> a()
    a
    

    Assigning anything to a.__call__ is pointless.

    However:

    >>> A.__call__ = b.__call__
    >>> a()
    b
    

    TLDR;

    a() does not call a.__call__. It calls type(a).__call__(a).

    Links

    There is a good explanation of why that happens in answer to "Why type(x).__enter__(x) instead of x.__enter__() in Python standard contextlib?".

    This behaviour is documented in Python documentation on Special method lookup.