Почему datetime.utcnow не ведет себя так, как я ожидаю от freezegun? - PullRequest
1 голос
/ 18 октября 2019

Я заметил кое-что, чего я не понимаю, и мне интересно, если бы кто-нибудь мог пролить свет на это.

Короче: если

  • x = datetime.datetime.utcnow

и

  • y = lambda: datetime.datetime.utcnow()

Я бы ожидал, что x() и y() будут вести себя одинаково, всегда. Тем не менее, это, очевидно, не тот случай, когда в игру вступает морозильник - он замораживает y, но не x, и мне интересно, почему. (Это верно, если x и y определены вне контекста freezegun, во всяком случае; внутри такого контекста, похоже, они ведут себя одинаково.)

Пример:

from datetime import datetime
import freezegun

# I'd expect these two to behave the same, always.
x = datetime.utcnow
y = lambda: datetime.utcnow()

with freezegun.freeze_time('2019-01-02 03:04:05'):
    # Here their behaviours diverge
    print('Time from x:', x())
    print('Time from y:', y())

    # This behaves as I'd expect, however.
    z = datetime.utcnow
    print('Time from z:', z())))

Результаты:

Time from x: 2019-10-18 12:21:37.508590
Time from y: 2019-01-02 03:04:05
Time from z: 2019-01-02 03:04:05

Здесь Time from x - время, в которое он запускается, т.е. он не находится под контролем freezegun.

Может кто-нибудь пролить свет на это? Это просто какая-то странность freezegun, или я неправильно понимаю что-то более фундаментальное в python, когда я предполагаю, что x и y всегда должны быть эквивалентны? Я вижу, что utcnow является методом связанного класса, но я не понимаю, почему это подразумевает такое поведение.


Postscript: time.time не ведет себя таким образом

Глядя на utcnow() источник это просто обертка вокруг time.time() - но time.time и lambda: time.time() не расходятся таким образом ... ТакЯ предполагаю, что имеет какое-то отношение к utcnow(), являющемуся связанным классом methiod - но я не знаю, что.

import time
import freezegun

r = time.time
s = lambda: time.time()

print('Time outside freezegun:', time.time())
with freezegun.freeze_time('2019-01-02 03:04:05'):
    print('Time from r:', r())
    print('Time from s:', s())

дает:

Time outside freezegun: 1571401765.2612312
Time from r: 1546398245.0
Time from s: 1546398245.0

Версии в игре

$ python --version
Python 3.7.3

$ pip list | grep freezegun
freezegun               0.3.12

1 Ответ

1 голос
/ 21 октября 2019

Так что я предполагаю, что это как-то связано с utcnow (), являющимся связанным классом methiod - но я не знаю, что.

Похоже, ваша интуиция верна,Freezegun не исправляет отдельные методы класса datetime - вместо этого он полностью заменяет класс своим собственным FakeDatetime классом.

Выполняя задание:

x = datetime.utcnow

x хранит ссылку на оригинальный метод utcnow(), который остается неизменным даже внутри freezegun.freeze_time() менеджера контекста.

С другой стороны, lambda: datetime.utcnow() вызывает utcnow() для datetime класса, которыйдоступно в текущем контексте, и это FakeDatetime исправлено freezegun.freeze_time().

time.time() - исправлено freezegun с fake_time(). Freezegun даже просматривает загруженные модули и исправляет переменные , сохраняя ссылку на time.time(), но он ограничивается переменными модуля, поэтому, например, он не проверяет внутренние списки:

import time
import freezegun

r = [time.time]

with freezegun.freeze_time('2019-01-02 03:04:05'):
    print('Time inside freezegun:', time.time())
    time_inside_list = r[0]
    print('Time from list:', time_inside_list())

output:

Time inside freezegun: 1546398245.0
Time from list: 1571669871.8807676

Бонус : Если freezegun так тщательно находит time.time() ссылок, хранящихся в переменных модуля, почему он не исправляет time.time(), используемый внутри datetime.utcnow()?

При поиске по sys.modules он намеренно пропускает модули datetime и time, чтобы не перекрывать функции источника, и как побочный эффект time.time, импортированный в модуль datetime, остается не исправленным.

...