/python-libfaketime

A fast alternative to freezegun that wraps libfaketime.

Primary LanguagePythonGNU General Public License v2.0GPL-2.0

python-libfaketime: fast date/time mocking

python-libfaketime is a wrapper of libfaketime for python.

Installation

Install with pip:

$ 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 get_tomorrow():
    return datetime.date.today() + datetime.timedelta(days=1)


@fake_time('2014-01-01 00:00:00')
def test_get_tomorrow():
    assert get_tomorrow() == datetime.date(2014, 1, 2)

It serves as a fast drop-in replacement for 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

Some brief details:

  • Linux and OS X, Pythons 2 and 3
  • 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.

Use with py.test

It's easiest to reexec from inside the pytest_configure hook:

# conftest.py
from libfaketime import reexec_if_needed

def pytest_configure():
    reexec_if_needed()

How to avoid re-exec

Sometimes, re-exec does unexpected things. You can avoid those problems by preloading libfaketime yourself. The environment variables you need 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 FAKETIME_DID_REEXEC=true

You can use them as such:

$ eval $(python-libfaketime)
$ nosetests  # ...or any other code that imports libfaketime

Known Issues

It was found that calling uuid.uuid1() multiple times while in a fake_time context could result in a deadlock. This situation only occured for users with a system level uuid1 library. In order to combat this issue, python-libfaketime temporarily disables the system level library by patching _uuid_generate_time to None while in the fake_time context.