Вы правы, процесс создания класса данных делает здесь что-то странное, что приводит к вашей текущей проблеме. Он связывает фабричную функцию во время создания класса, что означает, что он содержит ссылку на код до того, как freezegun сможет его исправить.
Вот пример без классов данных, которые сталкиваются с той же проблемой:
from datetime import datetime
from freezegun import freeze_time
class Foo:
# looks up the function at class creation time
now_func = datetime.now
def __init__(self):
# asks datetime for a reference at instance creation time
self.timestamp_a = datetime.now()
# uses an old reference we couldn't patch
self.timestamp_b = Foo.now_func()
with freeze_time(datetime(2020, 1, 1)):
foo = Foo()
assert foo.timestamp_a == datetime(2020, 1, 1) # works
assert foo.timestamp_b == datetime(2020, 1, 1) # raises an AssertionError
Что касается решения проблемы, вы можете теоретически взломать MyClass.__init__.__closure__
во время ваших тестов, чтобы отключить функции, но это немного безумие.
Что-то, что все еще немного лучше, чем перезапись timestamp
в __post_init__
, может заключаться в простом делегировании вызова функции с лямбда-выражением, так что поиск имени задерживается на время создания экземпляра:
timestamp: datetime = field(init=False, default_factory=lambda: datetime.now())
Или вы можете начать использовать другую библиотеку даты и времени, такую как маятник, которая поддерживает время замораживания из коробки . FWIW, это то, что я в итоге сделал.