Open hetsch opened 7 years ago
This looks definitely like a bug; it should be caught by https://github.com/FactoryBoy/factory_boy/blob/master/tests/test_using.py#L1652.
If you replace the call to timezone.now()
in your code by some "raise ValueError()", does it break during the test loading? Or later?
An option would be that some other factory is calling this iterator before entering your freezegun section; another would be related to the automated cycling of iterators by factory_boy — which reuse the values from the first run on subsequent calls.
I think it breaks when creating the models through the factory.
@factory.iterator
def starts_on():
raise ValueError()
dates = (
datetime(1970, 1, 1) - timedelta(days=3),
datetime(1970, 1, 1) - timedelta(days=3),
datetime(1970, 1, 1) - timedelta(days=4),
datetime(1970, 1, 1) + timedelta(days=1)
)
for date in dates:
yield date
give me the following error:
Test session starts (platform: darwin, Python 3.6.0, pytest 3.0.6, pytest-sugar 0.8.0)
rootdir: /Volumes/Data/Users/hetsch/Documents/twoshoes-core, inifile: setup.cfg
plugins: xdist-1.15.0, sugar-0.8.0, django-3.1.2, cov-2.4.0
―――――――――――――――――――――――――――――――――― ERROR at setup of TestTimeFramedMixin.test_defaults ―――――――――――――――――――――――――――――――――――
Traceback (most recent call last):
File "/Volumes/Data/Users/hetsch/Documents/twoshoes-core/tests/tests/test_models.py", line 179, in models
models = factories.TimeFramedMixinFactory.create_batch(4)
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/base.py", line 635, in create_batch
return [cls.create(**kwargs) for _ in range(size)]
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/base.py", line 635, in <listcomp>
return [cls.create(**kwargs) for _ in range(size)]
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/base.py", line 622, in create
attrs = cls.attributes(create=True, extra=kwargs)
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/base.py", line 453, in attributes
force_sequence=force_sequence,
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/containers.py", line 324, in build
return stub.__fill__()
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/containers.py", line 83, in __fill__
res[attr] = getattr(self, attr)
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/containers.py", line 108, in __getattr__
val = val.evaluate(self, self.__containers)
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/containers.py", line 227, in evaluate
containers=containers,
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/declarations.py", line 195, in evaluate
value = next(iter(self.iterator))
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/utils.py", line 158, in __iter__
value = next(self.iterator)
File "/Volumes/Data/Users/hetsch/Documents/twoshoes-core/tests/factories.py", line 149, in starts_on
raise ValueError()
ValueError
25% ██▌
―――――――――――――――――――――――――――――――――――― ERROR at setup of TestTimeFramedMixin.test_start ――――――――――――――――――――――――――――――――――――
Traceback (most recent call last):
File "/Volumes/Data/Users/hetsch/Documents/twoshoes-core/tests/tests/test_models.py", line 179, in models
models = factories.TimeFramedMixinFactory.create_batch(4)
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/base.py", line 635, in create_batch
return [cls.create(**kwargs) for _ in range(size)]
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/base.py", line 635, in <listcomp>
return [cls.create(**kwargs) for _ in range(size)]
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/base.py", line 622, in create
attrs = cls.attributes(create=True, extra=kwargs)
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/base.py", line 453, in attributes
force_sequence=force_sequence,
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/containers.py", line 324, in build
return stub.__fill__()
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/containers.py", line 83, in __fill__
res[attr] = getattr(self, attr)
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/containers.py", line 108, in __getattr__
val = val.evaluate(self, self.__containers)
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/containers.py", line 227, in evaluate
containers=containers,
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/declarations.py", line 195, in evaluate
value = next(iter(self.iterator))
File "/usr/local/var/pyenv/versions/3.6.0/envs/core-py3.6.0/lib/python3.6/site-packages/factory/utils.py", line 158, in __iter__
value = next(self.iterator)
StopIteration
@hetsch OK, found it — see the related commit. Basically, factory_boy saves values from the iterator on the first "run", and reuse them afterwards.
This is required, since Python doesn't allow to rewind generators...
For your case, an option would be the following.
Alternatively, we could try to change the behavior of factory.iterator
to be less surprising :)
def unrolled_iterator(func):
return factory.Iterator(func(), cycle=False)
@unrolled_iterator
def starts_on():
while True:
now = timezone.now()
dates = (
now - timedelta(days=3),
now - timedelta(days=3),
now - timedelta(days=4),
now + timedelta(days=1)
)
for date in dates:
yield date
@unrolled_iterator
def ends_on():
while True:
now = timezone.now()
dates = (
now + timedelta(days=3),
now,
now - timedelta(days=1),
now + timedelta(days=4)
)
for date in dates:
yield date
I'm currently using the
freezegun
package andfactoryboy
together in my unittests, what really worked well until I hit a problem with lazy evaluatingtimezone.now
in combination with@factory.iterator
. As far as I understood, the values of iterator fields are evaluated, when the factory class is imported/loaded by another module. This is kind of problematic for me, because I would like to freeze the (system) time so that all the model instances and their datefields (depending ontimezone.now
) have the exact same timestamp when they are created. Usually I have code like this:I have a model factory with two iterator fields that should generate four different datetime ranges, similar to this:
The problem is, that
now
get's evaluated when the whole class is loaded and not when the objects are generated. My question would be if there is any chance to trick the iterator, to actually evaluatenow
a little bit later, then when the model instances are´ generated by callingcreate_bulk
. I've triedlambda: timezone.now
and friends but no luck so far. What I have is this ugly hack based on a reference date (unix epoch) that i can later on (in _generate) replace with the actual timestamp:Sorry for the long post, I hope it is somewhat clear what I mean. I hope you can save me from this wild hocus-pocus.