python-libfaketime is a wrapper of libfaketime for python. Some brief details:
- Linux and OS X, Pythons 3.11 through 3.14, pypy and pypy3
- Mostly compatible with freezegun.
- Microsecond resolution.
- Accepts datetimes and strings that can be parsed by dateutil.
- Not threadsafe.
- Will break profiling. A workaround: use
libfaketime.{begin, end}_callbackto disable/enable your profiler (nosetest example).
$ pip install libfaketime
Wheels contain a native library and are platform-specific. If a compatible wheel
is not available, installation builds the library from source and requires make
and a C compiler.
import datetime from libfaketime import fake_time, reexec_if_needed # libfaketime needs to be preloaded by the dynamic linker. # This will exec the same command, but with the proper environment variables set. # You can also skip this and manually manage your env (see "How to avoid re-exec"). reexec_if_needed() def test_datetime_now(): # fake_time can be used as a context_manager with fake_time('1970-01-01 00:00:01'): # Every calls to a date or datetime function returns the mocked date assert datetime.datetime.utcnow() == datetime.datetime(1970, 1, 1, 0, 0, 1) assert datetime.datetime.now() == datetime.datetime(1970, 1, 1, 0, 0, 1) assert time.time() == 1 # fake_time can also be used as a decorator @fake_time('1970-01-01 00:00:01', tz_offset=12) def test_datetime_now_with_offset(): # datetime.utcnow returns the mocked datetime without offset assert datetime.datetime.utcnow() == datetime.datetime(1970, 1, 1, 0, 0, 1) # datetime.now returns the mocked datetime with the offset passed to fake_time assert datetime.datetime.now() == datetime.datetime(1970, 1, 1, 12, 0, 1)
By default, reexec_if_needed removes the LD_PRELOAD variable after the
re-execution, to keep your environment as clean as possible. You might want it
to stick around, for example when using parallelized tests that use subprocess
like pytest-xdist, and simply for tests where subprocess is called. To
keep them around, pass remove_vars=False like:
reexec_if_needed(remove_vars=False)
To avoid displaying the informative text when re-executing, you can set the
quiet parameter:
reexec_if_needed(quiet=True)
A common time can be shared between several execution contexts by using a file to store the time to mock, instead of environment variables. This is useful to control the time of a running process for instance. Here is a schematized use case:
reexec_if_needed(remove_vars=False) with fake_time("1970-01-01 00:00:00", timestamp_file="/tmp/timestamp"): subprocess.run("/some/server/process") with fake_time("2000-01-01 00:00:00", timestamp_file="/tmp/timestamp"): assert request_the_server_process_date() == "2000-01-01 00:00:00"
libfaketime tends to be significantly faster than freezegun. Here's the output of a totally unscientific benchmark on my laptop:
$ python benchmark.py re-exec with libfaketime dependencies timing 1000 executions of <class 'libfaketime.fake_time'> 0.021755 seconds $ python benchmark.py freezegun timing 1000 executions of <function freeze_time at 0x10aaa1140> 6.561472 seconds
The pytest-libfaketime plugin will automatically configure python-libfaketime for you:
$ pip install pytest-libfaketime
Alternatively, you can reexec manually from inside the pytest_configure hook:
# conftest.py import os import libfaketime def pytest_configure(): libfaketime.reexec_if_needed() _, env_additions = libfaketime.get_reload_information() os.environ.update(env_additions)
In your tox configuration file, under the testenv bloc, add the libfaketime environment variables to avoid re-execution:
setenv = LD_PRELOAD = {envsitepackagesdir}/libfaketime/vendor/libfaketime/src/libfaketime.so.1 DONT_FAKE_MONOTONIC = 1 FAKETIME_DID_REEXEC = true
python-libfaketime should have the same behavior as freezegun when running on supported code. To migrate to it, you can run:
find . -type f -name "*.py" -exec sed -i 's/freezegun/libfaketime/g' "{}" \;
In some cases - especially when your tests start other processes - re-execing can cause unexpected problems. To avoid this, you can preload the necessary environment variables yourself. The necessary environment for your system can be found by running python-libfaketime on the command line:
$ python-libfaketime export LD_PRELOAD="/home/foo/<snip>/vendor/libfaketime/src/libfaketime.so.1" export DONT_FAKE_MONOTONIC="1" export FAKETIME_NO_CACHE="1" export FAKETIME_DID_REEXEC=true
You can easily put this in a script like:
$ eval $(python-libfaketime) $ pytest # ...or any other code that imports libfaketime
Contributions are welcome! You should compile libfaketime before running tests:
git submodule init --update # For Linux: env FAKETIME_COMPILE_CFLAGS="-UFAKE_STAT -UFAKE_UTIME -UFAKE_SLEEP -Wno-unused-function" make -C libfaketime/vendor/libfaketime # For macOS env make -C libfaketime/vendor/libfaketime
Then you can install requirements with pip install -r requirements.txt and use pytest and tox to run the tests.
Calling uuid.uuid1() multiple times while in a fake_time context can result in a deadlock when an OS-level uuid library is available.
To avoid this, python-libtaketime will monkeypatch uuid._uuid_generate_time (or similar, it varies by version) to None inside a fake_time context.
This may slow down uuid1 generation but should not affect correctness.