Open alex-kowalczyk opened 4 years ago
Can you confirm which version of freezegun you are using?
By default, moto should be not running when it sees threading in the stacktrace: https://github.com/spulec/freezegun/commit/270e3cae8216878d4c83d3d39001e37ff457649d
See https://github.com/spulec/freezegun/issues/307 for why that is also an issue.
0.3.12 , so it includes the commit.
The issue is not with ignoring threading
itself. The problem occurs when the thread still runs and uses time functions (eg time.time()) after the main thread has been unfrozen (exited with
block). time.time() checks if it should be ignored while the ignore_lists is already removed.
Ah, I see.
If we just check for the presence of ignore_lists[-1]
before accessing it, I believe the code will still break later on. We could return True
is ignore_lists
is empty. Is that the behavior we want though? As a user, would you expect this thread to be using the real time or the fake time at this point?
Ideally the threads spawned in frozen-time should be frozen, to make this predictable.
Any workaround for this? It really hurts.
I hit the same issue and after upgrading to 1.1.0, it was fixed.
This is a simple multi-threading bug caused by not locking around accesses of the global freezegun.api.ignore_lists
.
For example:
freezegun.api.ignore_lists
._should_use_real_time()
which first checks if ignore_lists
is empty. It's not empty, so we continue.ignore_lists
._should_use_real_time()
, tries to use ignore_lists[-1]
. ignore_lists
is now empty, so an IndexError
is raised.It doesn't actually matter if we've correctly configured to ignore the affected module. The attempt to check for ignored modules is failing before we can make a decision.
One option is to add locking around the most of _should_use_real_time()
and the .append()
and .pop()
calls. As long as we're only freezing time for testing purposes, any lock contention should be negligible.
Another option is to fix this locklessly with an EAFP approach. At the top of _should_use_real_time()
, try to grab ignore_lists[-1]
. If it raises and IndexError
, then we're not in a frozen context, so return True
. Else, hold onto the value we just got and use it for the rest of the function.
This would look something like:
def _should_use_real_time():
...
try:
modules_to_ignore = ignore_lists[-1]
except IndexError:
# Means stop() has already been called, so we can now return the real time
return True
for ...:
...
if ... module_name.startswith(modules_to_ignore):
return True
...
This avoid a lock, but the modules_to_ignore
value could become stale while using it. Again, this shouldn't be a big deal if we're only testing.
Also, this problem exists for all of the mutable globals: https://github.com/spulec/freezegun/blob/1af533984e118f5fe58d3264a52c904737df7d77/freezegun/api.py#L60-L63
Some of these look like they need a larger refactor to make threadsafe.
When fake_time or similar patched thing is somehow passed (eg via queue) to some thread, it might be later called there. In the meanwhile the patching might have been removed (e.g main thread exited
with freeze_time()
block). This resuls in ignore_lists or freeze_factories being empty.This bugs hits logging background transports heavily.
Quick and dirty solution would be to check for presence of the element [-1] before accessing it.
This results in an exception like these: