A fast alternative to freezegun that wraps libfaketime.
Project description
python-libfaketime: fast date/time mocking
python-libfaketime is a wrapper of libfaketime for python. Some brief details:
- Linux and OS X, Pythons 3.8 through 3.12, 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}_callback
to disable/enable your profiler (nosetest example).
Installation
$ pip install libfaketime
Usage
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)
remove_vars
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)
quiet
To avoid displaying the informative text when re-executing, you can set the
quiet
parameter:
reexec_if_needed(quiet=True)
timestamp_file
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"
Performance
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
Use with py.test
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)
Use with tox
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
Migration from freezegun
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' "{}" \;
How to avoid re-exec
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
Contributing and testing
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" 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.
uuid1 deadlock
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.
Changelog
Semantic versioning is used.
2.1.0
released 2024-05-17
Thanks for @azmeuk for all their contributions to this release!
- add support for timestamp files, which enables freezing time across subprocesses: #78
- upgrade underlying libfaketime to 0.9.10 without modifications: #75
- add a quiet param to rexec_if_needed: #72
2.0.0
released 2020-04-17
- breaking: drop python 2.7 support
- set LD_LIBRARY_PATH on linux to support paths containing spaces: #57
- fix compatibility with non-pytz tzinfo objects: #58
1.2.1
released 2019-01-20
- fix a deadlock on python 3.7+
1.2.0
released 2018-10-28
- offset-aware datetimes now properly fake the timezone as well: #49
1.1.0
released 2018-10-07
- decorated classes can access the fake_time object with
self._faked_time
: #47
1.0.0
released 2018-06-16
- backwards incompatible: the monotonic clock is no longer mocked: #45
- ensure TZ is set to a valid timezone: #46
0.5.2
released 2018-05-19
- fix a bug causing incorrect times after unpatching under python 3.6+: #43
- fix compilation under gcc8: #44
0.5.1
released 2018-01-19
- fix usage as a class decorator : #41
0.5.0
released 2017-09-10
0.4.4
released 2017-07-16
- allow contextlib2 as an alternative to contextdecorator: #30
0.4.3
released 2017-07-07
- add macOS Sierra compatibility: #29
0.4.2
released 2016-06-30
- fix only_main_thread=False: #24
0.4.1
released 2016-05-02
- fix deadlocks from uuid.uuid1 when faking time: #14
- remove contextdecorator dependency on python3: #15
0.4.0
released 2016-04-02
- freezegun's tick() is now supported; see their docs for usage.
0.3.0
released 2016-03-04
- invoking
libfaketime
from the command line will now print the necessary environment to avoid a re-exec.
0.2.1
released 2016-03-01
- python 3 support
0.1.1
released 2015-09-11
- prevent distribution of test directory: https://github.com/simon-weber/python-libfaketime/pull/4
0.1.0
released 2015-06-23
- add global start/stop callbacks
0.0.3
released 2015-03-28
- initial packaged release
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for libfaketime-2.1.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | df72634f79900e71160f4e3ba4cf8f1d7ba6167e7c9a4f28d67c08e0a4f03ba2 |
|
MD5 | f733fd47d7b78fa9e2a354cf1783f424 |
|
BLAKE2b-256 | 0312c28cdc2834bbd71e9300f97093766dfcd4b8f3c7b7059c1e8543c6f3b14c |