Package: immobilus
A simple time freezing tool for python tests. It mocks:datetime.date.today()datetime.datetime.now()datetime.datetime.utcnow()datetime.datetime.fromtimestamp()time.time()time.gmtime()time.localtime()time.strftime()time.mktime()UsageYou mustimport immobilusbeforedatetimeortime, or any
other module which imports them in turn, to allow it to intercept those
modules.>>>fromimmobilusimportimmobilus>>>fromdatetimeimportdatetime,timedeltaFor example, if you usepytest, you could addimport immobilusinto your rootconftest.pyfile.Context managerYou can useimmobilusas a context manager. When the context manager
is active, time is frozen to the specified value. Outside of the context
manager, the original standard library functions are used and time
behaves normally.>>># It is unlikely that you are living in the past>>>datetime.utcnow()==datetime(2017,10,20)False>>># But with immobilus, you can pretend that you are>>>withimmobilus('2017-10-20'):...datetime.utcnow()==datetime(2017,10,20)...True>>># Once the context manager exits, immobilus deactivates.>>># We are back in the present.>>>datetime.utcnow()==datetime(2017,10,20)FalseSpecifying the freeze timeAs shown above, you can use a string to describe the time to be frozen
(e.g.'2017-10-20'). Any values understood by thedateutil.parsercan be used.You can also use adatetime.datetimeobject for the freeze time:>>>naive_freeze_time=datetime(2017,10,20)>>>withimmobilus(naive_freeze_time):...print('now:%s'%datetime.now())...print('utcnow:%s'%datetime.utcnow())...now:2017-10-2000:00:00utcnow:2017-10-2000:00:00immobiluswill use the givendatetimeobject to set the frozen
UTC timeandlocal time to the same value that you provide. If thedatetimeyou provide for the freeze time is aware, then it is
adjusted to UTC like so:>>>importpytz>>>>>># Freeze to 12:00 noon in Moscow (UTC+3)>>>timezone=pytz.timezone('Europe/Moscow')>>>aware_freeze_time=timezone.localize(datetime(2017,10,20,12))>>>withimmobilus(aware_freeze_time):...# 9:00am local time which is same as UTC...print('now:%s'%datetime.now())...print('utcnow:%s'%datetime.utcnow())...now:2017-10-2009:00:00utcnow:2017-10-2009:00:00If you want local time to differ from UTC, read on.Freezing a local time that is not UTC timeTo have a different timezone in effect when time is frozen, use the
second argument to theimmobiluscontext manager:tz_offset.
This is the number of hours ahead of the frozen UTC time that the frozen
local time should be.>>>withimmobilus('2017-10-20 09:00',tz_offset=3):...print('now:%s'%datetime.now())...print('utcnow:%s'%datetime.utcnow())...now:2017-10-2012:00:00utcnow:2017-10-2009:00:00Of course, you can be behind UTC if you wish, by using a negative
number:>>>withimmobilus('2017-10-20 09:00',tz_offset=-7):...print('now:%s'%datetime.now())...print('utcnow:%s'%datetime.utcnow())...now:2017-10-2002:00:00utcnow:2017-10-2009:00:00You can move the frozen time point by calling thetickmethod:>>>withimmobilus('2019-08-21 12:00:00')asdt:...print(datetime.now())...dt.tick()...print(datetime.now())...dt.tick(timedelta(seconds=10))...print(datetime.now())...2019-08-2112:00:002019-08-2112:00:012019-08-2112:00:11Using as a decoratorAs well as being a context manager,immobilusis also a decorator:>>>@immobilus('2017-10-20')...deftest():...print(datetime.now())...>>>test()2017-10-2000:00:00It works even with classes>>>@immobilus('2017-10-20')...classDecorated(object):...now=datetime.utcnow()......deffirst(self):...returndatetime.utcnow()......defsecond(self):...returnself.now...>>>d=Decorated()>>>assertd.first().strftime('%Y-%m-%d%H:%M:%S')=='2017-10-20 00:00:00'>>>assertd.second().strftime('%Y-%m-%d%H:%M:%S')!='2017-10-20 00:00:00'and coroutines>>>importasyncio>>>>>>@immobilus('2017-10-20')...asyncdeftest():...returndatetime.now()...>>>loop=asyncio.new_event_loop()>>>result=loop.run_until_complete(test())>>>>>>assertresult.strftime('%Y-%m-%d%H:%M:%S')=='2017-10-20 00:00:00'Using directlyOr you can activate and deactivateimmobilusmanually.>>>freeze_time=datetime(2017,10,20)>>>spell=immobilus(freeze_time)>>>datetime.utcnow()==freeze_timeFalse>>>spell.start()FakeDatetime(2017,10,20,0,0)>>>datetime.utcnow()==freeze_timeTrue>>>datetime.utcnow()FakeDatetime(2017,10,20,0,0)>>>spell.stop()>>>datetime.utcnow()==freeze_timeFalseThis can be quite useful for those using the standard libraryunittest.TestCasee.g.importunittestclassSomeTests(unittest.TestCase):defsetUp(self):spell=immobilus('2017-10-20')spell.start()self.addCleanup(spell.stop)NestingYou can also nest context managers (or decorators, or direct
invocations, or any combination) if you want to freeze different times.>>>withimmobilus('2017-10-20 12:00'):...print('outer now:%s'%datetime.now())...print('outer utcnow:%s'%datetime.utcnow())...withimmobilus('2017-10-21 12:00',tz_offset=5):...print('inner now:%s'%datetime.now())...print('inner utcnow:%s'%datetime.utcnow())...print('outer now:%s'%datetime.now())...print('outer utcnow:%s'%datetime.utcnow())...outernow:2017-10-2012:00:00outerutcnow:2017-10-2012:00:00innernow:2017-10-2117:00:00innerutcnow:2017-10-2112:00:00outernow:2017-10-2012:00:00outerutcnow:2017-10-2012:00:00Special thanks for contribution:Eloi Rivard (https://github.com/azmeuk)Day Barr (https://github.com/daybarr)