[ИЗМЕНИТЬ]
Возможно, наиболее интересной частью этого вопроса является Почему я не могу исправить somefunction.__call__
?
Потому что функция не использует код __call__
, а __call__
(объект-оболочка метода) использует код функции.
Я не нашел никакой документации по этому поводу, но я могу доказать это (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>
Замените код f
кодом g
:
>>> f.func_code = g.func_code
>>> f()
'g'
>>> f.__call__()
'g'
Конечно, ссылки f
и f.__call__
не изменяются:
>>> f
<function f at 0x7f1576381848>
>>> f.__call__
<method-wrapper '__call__' of function object at 0x7f1576381848>
Восстановите исходную реализацию и вместо этого скопируйте ссылки __call__
:
>>> def f():
... return "f"
...
>>> f()
'f'
>>> f.__call__ = g.__call__
>>> f()
'f'
>>> f.__call__()
'g'
Это не влияет на функцию f
. Примечание. В Python 3 следует использовать __code__
вместо func_code
.
Я надеюсь, что кто-нибудь может указать мне на документацию, объясняющую такое поведение.
У вас есть способ обойти это: в utils
вы можете определить
class Utcnow(object):
def __call__(self):
return datetime.datetime.utcnow()
utcnow = Utcnow()
И теперь ваш патч может работать как шарм.
Следуйте исходному ответу, который я считаю даже лучшим способом реализации ваших тестов.
У меня есть собственное золотое правило: никогда не исправлять защищенные методы. В этом случае все немного проще, потому что защищенный метод был введен только для тестирования, но я не понимаю, почему.
Настоящая проблема здесь в том, что вы не можете напрямую исправить datetime.datetime.utcnow
(это расширение C, как вы написали в комментарии выше). Что вы можете сделать, так это исправить datetime
, обернув стандартное поведение и переопределив функцию utcnow
:
>>> with mock.patch("datetime.datetime", mock.Mock(wraps=datetime.datetime, utcnow=mock.Mock(return_value=3))):
... print(datetime.datetime.utcnow())
...
3
Хорошо, это не очень понятно и аккуратно, но вы можете ввести свою собственную функцию, например
def mock_utcnow(return_value):
return mock.Mock(wraps=datetime.datetime,
utcnow=mock.Mock(return_value=return_value)):
и сейчас
mock.patch("datetime.datetime", mock_utcnow(***))
делайте именно то, что вам нужно, без какого-либо другого слоя и для каждого вида импорта.
Другим решением может быть импорт datetime
в utils
и исправление ***.utils.datetime
; это может дать вам некоторую свободу для изменения эталонной реализации datetime
без изменения ваших тестов (в этом случае также позаботьтесь об изменении аргумента mock_utcnow()
wraps
).
person
Michele d'Amico
schedule
18.12.2015
with mock.patch('***.utils.utcnow', return_value=***): do_something()
? - person Łukasz Rogalski   schedule 14.12.2015from ***.utils import utcnow
содержит ссылку на исходную реализацию. - person warvariuc   schedule 14.12.2015mock.patch('module.which.imported.utcnow')
. Это то, что вы хотите? Ваша проблема не в исправлении__call__
, а в пространствах имен Python, механизмах импорта и разрешения имен. - person Łukasz Rogalski   schedule 14.12.2015datetime.datetime.utcnow
напрямую? почему ты этого не сделал? Я ненавижу исправлять внутренний или защищенный метод. - person Michele d'Amico   schedule 18.12.2015datetime.datetime
- person Michele d'Amico   schedule 18.12.2015__call__
не работает. - person Michele d'Amico   schedule 18.12.2015