Skip to content

Commit

Permalink
Add support for time.monotonic()
Browse files Browse the repository at this point in the history
  • Loading branch information
Petr Šebek committed Jul 13, 2017
1 parent 385a78d commit 2c1a724
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
25 changes: 25 additions & 0 deletions freezegun/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
real_date = datetime.date
real_datetime = datetime.datetime

# monotonic is available since python 3.3
try:
real_monotonic = time.monotonic
except AttributeError:
real_monotonic = None

try:
real_uuid_generate_time = uuid._uuid_generate_time
except ImportError:
Expand Down Expand Up @@ -222,6 +228,18 @@ def _tz_offset(cls):
FakeDatetime.max = datetime_to_fakedatetime(real_datetime.max)


class FakeMonotonic(object):
def __init__(self, time_to_freeze, previous_monotonic_function):
self.time_to_freeze = time_to_freeze
self.previous_monotonic_function = previous_monotonic_function
self._start_freeze_time = self.time_to_freeze()
self._start_monotonic = self.previous_monotonic_function()

def __call__(self):
current_time = self.time_to_freeze()
return self._start_monotonic + (current_time - self._start_freeze_time).total_seconds()


def convert_to_timezone_naive(time_to_freeze):
"""
Converts a potentially timezone-aware datetime to be a naive UTC datetime
Expand Down Expand Up @@ -404,6 +422,11 @@ def start(self):
('real_strftime', real_strftime, 'FakeStrfTime', fake_strftime),
('real_time', real_time, 'FakeTime', fake_time),
]
if real_monotonic is not None:
fake_monotonic = FakeMonotonic(time_to_freeze, time.monotonic)
time.monotonic = fake_monotonic
to_patch.append(('real_monotonic', real_monotonic, 'FakeMonotonic', fake_monotonic))

real_names = tuple(real_name for real_name, real, fake_name, fake in to_patch)
self.fake_names = tuple(fake_name for real_name, real, fake_name, fake in to_patch)
self.reals = dict((id(fake), real) for real_name, real, fake_name, fake in to_patch)
Expand Down Expand Up @@ -490,6 +513,8 @@ def stop(self):
time.gmtime = time.gmtime.previous_gmtime_function
time.localtime = time.localtime.previous_localtime_function
time.strftime = time.strftime.previous_strftime_function
if real_monotonic is not None:
time.monotonic = time.monotonic.previous_monotonic_function

uuid._uuid_generate_time = real_uuid_generate_time
uuid._UuidCreate = real_uuid_create
Expand Down
54 changes: 54 additions & 0 deletions tests/test_datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,43 @@ def test_move_to():
assert frozen_datetime() == initial_datetime


def test_manual_increment_monotonic():
if sys.version_info[0] != 3:
raise skip.SkipTest("test target is Python3")

initial_datetime = datetime.datetime(year=1, month=7, day=12,
hour=15, minute=6, second=3)
with freeze_time(initial_datetime) as frozen_datetime:
initial_monotonic = time.monotonic()

frozen_datetime.tick()
initial_monotonic += 1
assert time.monotonic() == initial_monotonic

frozen_datetime.tick(delta=datetime.timedelta(seconds=10))
initial_monotonic += 10
assert time.monotonic() == initial_monotonic


def test_move_to_monotonic():
if sys.version_info[0] != 3:
raise skip.SkipTest("test target is Python3")

initial_datetime = datetime.datetime(year=1, month=7, day=12,
hour=15, minute=6, second=3)

other_datetime = datetime.datetime(year=2, month=8, day=13,
hour=14, minute=5, second=0)
with freeze_time(initial_datetime) as frozen_datetime:
initial_monotonic = time.monotonic()

frozen_datetime.move_to(other_datetime)
assert time.monotonic() - initial_monotonic == (other_datetime - initial_datetime).total_seconds()

frozen_datetime.move_to(initial_datetime)
assert time.monotonic() == initial_monotonic


def test_bad_time_argument():
try:
freeze_time("2012-13-14", tz_offset=-4)
Expand Down Expand Up @@ -394,6 +431,23 @@ def test_nested_context_manager():
assert datetime.datetime.now() > datetime.datetime(2013, 1, 1)


def test_nested_monotonic():
if sys.version_info[0] != 3:
raise skip.SkipTest("test target is Python3")

with freeze_time('2012-01-14') as frozen_datetime_1:
initial_monotonic_1 = time.monotonic()
with freeze_time('2012-12-25') as frozen_datetime_2:
initial_monotonic_2 = time.monotonic()
frozen_datetime_2.tick()
initial_monotonic_2 += 1
assert time.monotonic() == initial_monotonic_2
assert time.monotonic() == initial_monotonic_1
frozen_datetime_1.tick()
initial_monotonic_1 += 1
assert time.monotonic() == initial_monotonic_1


def _assert_datetime_date_and_time_are_all_equal(expected_datetime):
assert datetime.datetime.now() == expected_datetime
assert datetime.date.today() == expected_datetime.date()
Expand Down
14 changes: 14 additions & 0 deletions tests/test_ticking.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import datetime
import time
import mock
import sys

from nose.plugins import skip

from freezegun import freeze_time
from tests import utils
Expand Down Expand Up @@ -43,3 +46,14 @@ def test_non_pypy_compat():
freeze_time("Jan 14th, 2012, 23:59:59", tick=True)
except Exception:
raise AssertionError("tick=True should not error on CPython")


@utils.cpython_only
def test_ticking_datetime_monotonic():
if sys.version_info[0] != 3:
raise skip.SkipTest("test target is Python3")

with freeze_time("Jan 14th, 2012", tick=True):
initial_monotonic = time.monotonic()
time.sleep(0.001) # Deal with potential clock resolution problems
assert time.monotonic() > initial_monotonic

0 comments on commit 2c1a724

Please sign in to comment.