21

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?

Dag Høidahl
  • 7,873
  • 8
  • 53
  • 66
warvariuc
  • 57,116
  • 41
  • 173
  • 227
  • What's wrong with `with mock.patch('***.utils.utcnow', return_value=***): do_something()`? – Łukasz Rogalski Dec 14 '15 at 07:02
  • @Rogalski it won't work for cases when user code did `from ***.utils import utcnow` holding reference to the original implementation. – warvariuc Dec 14 '15 at 07:03
  • In this case you should use `mock.patch('module.which.imported.utcnow')`. Is that what you want? Your problem is not about patching `__call__`, is about Python namespaces, imports and name resolving mechanisms. – Łukasz Rogalski Dec 14 '15 at 07:13
  • Yes, I could do like this, but then I should track each such module and possibly patch several modules in one single test. I would rather patch one single place. – warvariuc Dec 14 '15 at 07:19
  • And what about to patch `datetime.datetime.utcnow` directly? why you didn't it? I hate to patch internal or protected method. – Michele d'Amico Dec 18 '15 at 12:54
  • @Micheled'Amico > First, datetime.datetime is written in C, so Mock can't replace attributes on the class, so you can't simply mock out just the today() function. nedbatchelder.com/blog/201209/mocking_datetimetoday.html – warvariuc Dec 18 '15 at 13:12
  • @warvariuc or patch all `datetime.datetime` – Michele d'Amico Dec 18 '15 at 13:18
  • I am aware of all other solutions. I even use one already, as stated in the question. I just want to know if the current solution can simpler. – warvariuc Dec 18 '15 at 13:33
  • @warvariuc , Ok I covered why patching `__call__` desn't work. – Michele d'Amico Dec 18 '15 at 19:45
  • @warvariuc Consider to change your accepted answer to zvone's one. His is the correct one, mine was just a try to explain something that I didn't understand. – Michele d'Amico Dec 24 '15 at 08:25

3 Answers3

14

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.

zvone
  • 18,045
  • 3
  • 49
  • 77
  • Thank you very much. That should be the accepted answer, it explain exactly what happen. Do you know if it is the standard behavior of every magic method or just the __call__'s one? – Michele d'Amico Dec 24 '15 at 09:20
  • @Micheled'Amico It *looks* like all of them behave that way (those I tried, at least), but actually I did not see any documentation saying that it should be so. – zvone Dec 24 '15 at 09:35
  • @Micheled'Amico Looking at the python source, I would say that the type slots defined in [`slots.py`](http://svn.python.org/projects/python/trunk/Tools/framer/framer/slots.py) are actually the list of things which are always called on the type rather than on the instance. This is an approximation, because I am too lazy to analyse more deeply ;) Anyway, for *calling* an object, Pyhon actually does `call = func->ob_type->tp_call` in [`abstract.c`](http://svn.python.org/projects/python/trunk/Objects/abstract.c) – zvone Dec 24 '15 at 10:26
  • 3
    Found another question with a better explanation: http://stackoverflow.com/questions/34490998/why-typex-enter-x-instead-of-x-enter-in-python-standard-context – warvariuc Dec 28 '15 at 10:20
13

[EDIT]

Maybe the most interesting part of this question is Why I cannot patch somefunction.__call__?

Because the function don't use __call__'s code but __call__ (a method-wrapper object) use function's code.

I don't find any well sourced documentation about that, but I can prove it (Python2.7):

>>> def f():
...     return "f"
... 
>>> def g():
...     return "g"
... 
>>> f
<function f at 0x7f1576381848>
>>> f.__call__
<method-wrapper '__call__' of function object at 0x7f1576381848>
>>> g
<function g at 0x7f15763817d0>
>>> g.__call__
<method-wrapper '__call__' of function object at 0x7f15763817d0>

Replace f's code by g's code:

>>> f.func_code = g.func_code
>>> f()
'g'
>>> f.__call__()
'g'

Of course f and f.__call__ references are not changed:

>>> f
<function f at 0x7f1576381848>
>>> f.__call__
<method-wrapper '__call__' of function object at 0x7f1576381848>

Recover original implementation and copy __call__ references instead:

>>> def f():
...     return "f"
... 
>>> f()
'f'
>>> f.__call__ = g.__call__
>>> f()
'f'
>>> f.__call__()
'g'

This don't have any effect on f function. Note: In Python 3 you should use __code__ instead of func_code.

I Hope that somebody can point me to the documentation that explain this behavior.

You have a way to work around that: in utils you can define

class Utcnow(object):
    def __call__(self):
        return datetime.datetime.utcnow()


utcnow = Utcnow()

And now your patch can work like a charm.


Follow the original answer that I consider even the best way to implement your tests.

I've my own gold rule: never patch protected methods. In this case the things are little bit smoother because protected method was introduced just for testing but I cannot see why.

The real problem here is that you cannot to patch datetime.datetime.utcnow directly (is C extension as you wrote in the comment above). What you can do is to patch datetime by wrap the standard behavior and override utcnow function:

>>> with mock.patch("datetime.datetime", mock.Mock(wraps=datetime.datetime, utcnow=mock.Mock(return_value=3))):
...  print(datetime.datetime.utcnow())
... 
3

Ok that is not really clear and neat but you can introduce your own function like

def mock_utcnow(return_value):
    return mock.Mock(wraps=datetime.datetime, 
                     utcnow=mock.Mock(return_value=return_value)):

and now

mock.patch("datetime.datetime", mock_utcnow(***))

do exactly what you need without any other layer and for every kind of import.

Another solution can be import datetime in utils and to patch ***.utils.datetime; that can give you some freedom to change datetime reference implementation without change your tests (in this case take care to change mock_utcnow() wraps argument too).

Michele d'Amico
  • 22,111
  • 8
  • 69
  • 76
4

As commented on the question, since datetime.datetime is written in C, Mock can't replace attributes on the class (see Mocking datetime.today by Ned Batchelder). Instead you can use freezegun.

$ pip install freezegun

Here's an example:

import datetime

from freezegun import freeze_time

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


@freeze_time('2000-01-01 12:00:01')
def test_freezegun():
    assert my_now() == datetime.datetime(2000, 1, 1, 12, 00, 1)

As you mention, an alternative is to track each module importing datetime and patch them. This is in essence what freezegun does. It takes an object mocking datetime, iterates through sys.modules to find where datetime has been imported and replaces every instance. I guess it's arguable whether you can do this elegantly in one function.

Dag Høidahl
  • 7,873
  • 8
  • 53
  • 66
  • Yes, I am aware of this tool. But I don't wan't to use it -- for me it's more explicit and simpler to use an intermediate function. – warvariuc Dec 18 '15 at 08:08
  • If you add info why patching `__call__` is not possible, I will accept your answer. – warvariuc Dec 18 '15 at 14:11
  • I meant why I can't patch `__call__` with `mock` -- I guess it's because FunctionType has also C implementation. – warvariuc Dec 18 '15 at 16:35
  • 2
    @warvariuc: beware, [`freezegun`'s timezone support was broken last time I've checked](http://stackoverflow.com/questions/31296798/python-freezegun-giving-different-values-when-freezing-time-to-datetime-datetime#comment50600538_31296798) – jfs Dec 18 '15 at 18:02
  • @J.F.Sebastian, thanks for the info. Dag, I've accepted the other answer as it provides the answer for my question. – warvariuc Dec 19 '15 at 15:20
  • My answer was an answer to your problem, not your question, so that's fair enough. – Dag Høidahl Dec 19 '15 at 15:25