freezegun
freezegun copied to clipboard
Improve asyncio support to avoid hangs of asyncio.sleep()
Clean up asyncio tests
We finish the cleanup of test_asyncio.py
done in c60dd14b05f09c482d4:
- We remove
test_time_freeze_async_def
because since c60dd14b05f09c48 it is exactly the same astest_time_freeze_coroutine
. - We remove remnants of support of Pythons with no
asyncio
module because all Python interpreters supported by freezegun already supportasyncio
.
Remove Python 3.6 from tox.ini
PR #455 removed support for Python 3.6 so we remove it from tox.ini
.
This should allow us to use Python 3.7+-only features, like asyncio.run
.
Avoid warning when running FreezeGun tests on Python 3.10+
To avoid this warning:
freezegun/tests/test_asyncio.py:12: DeprecationWarning: There is no current event loop
asyncio.get_event_loop().run_until_complete(frozen_coroutine())
on Python 3.10+, we modify tests to use asyncio.run
instead of
asyncio.get_event_loop().run_until_complete
, which [1] recommends:
asyncio.get_event_loop()
(...)
Consider also using the asyncio.run() function instead of using
lower level functions to manually create and close an event loop.
Deprecated since version 3.10: Deprecation warning is emitted
if there is no running event loop. In future Python releases,
this function will be an alias of get_running_loop().
asyncio.run
has been added in Python 3.7 but we no longer support 3.6
(see #455) so we can use it.
[1] https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_event_loop
Avoid asyncio.sleep()
hanging forever when time is frozen
The following code:
async def test():
with freeze_time("2020-01-01"):
await asyncio.sleep(0.01)
hangs forever since FreezeGun 1.1.0 because 1.1.0 started patching
time.monotonic()
(see #369) which is used internally by asyncio
event loops to schedule code for execution in the future. This breaks
many projects that uses FreezeGun to test asynchronous code.
We fix this by changing freeze_time
to patch asyncio event loop's
time()
method in a way that it uses real monotonic time instead of the
frozen one. Note that we couldn't achieve this by adding asyncio
to
DEFAULT_IGNORE_LIST
in freezegun/config.py
because any running async
code has functions from the asyncio
module on its stack -- adding
asyncio
to the ignore list would just disable freezing time in any
async code. This is why we patch one method of a specific class instead.
This change not only fixes asyncio.sleep()
but also things like
asyncio.get_running_loop().call_later
(for scheduling task execution
in the future) which in turn makes things like timeouts work in async
code while time is frozen. This may not be desired because some users
may expect that execution of events scheduled to happen in the future
can be controlled using FreezeGun. However, it's not easy to distinguish
between things that users would like to see frozen time and those which
should not (like asyncio.sleep()
) because all of them use the same
clock. Therefore, we opt for making all asyncio
internals not affected
by FreezeGun.
We also add more tests that verify how FreezeGun interacts with asyncio code, including tests that cover the scenario described in #437 which we aim to fix.
Closes #401 Closes #437