Repository: adamchainz/time-machine

Issue:
Freeze time instead of just shifting it
Hi! This library looks very cool, even more with the added support for pytest. I have a question which might be more related with me not understanding the use cases or purpose than the tool itself.

I thought I would be able to tell the library: everytime I call now I want you to return this. Instead what I found is that when I call the `move_to` method in the time_machine fixture, time goes back to that time but the starts moving forward accurately. What I wanted to be able to do is to always get the same value when utcnow is called so I can test the value of queries that I am generating on the fly.

Is this possible? Thanks!


Comments:

Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 3265158..5390cf4 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -2,6 +2,10 @@
 History
 =======
 
+* Allow passing ``tick`` to ``Coordinates.move_to()`` and the pytest fixture’s
+  ``time_machine.move_to()``. This allows freezing or unfreezing of time when
+  travelling.
+
 2.2.0 (2021-07-02)
 ------------------
 
diff --git a/README.rst b/README.rst
index c4dc27b..d95ff70 100644
--- a/README.rst
+++ b/README.rst
@@ -235,12 +235,14 @@ Here’s a worked example changing the current timezone:
 The ``start()`` method and entry of the context manager both return a ``Coordinates`` object that corresponds to the given "trip" in time.
 This has a couple methods that can be used to travel to other times.
 
-``move_to(destination)``
-^^^^^^^^^^^^^^^^^^^^^^^^
+``move_to(destination, tick=None)``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 ``move_to()`` moves the current time to a new destination.
 ``destination`` may be any of the types supported by ``travel``.
 
+``tick`` may be set to a boolean, to change the ``tick`` flag of ``travel``.
+
 For example:
 
 .. code-block:: python
diff --git a/src/time_machine/__init__.py b/src/time_machine/__init__.py
index 7f297a9..a8302b8 100644
--- a/src/time_machine/__init__.py
+++ b/src/time_machine/__init__.py
@@ -163,12 +163,18 @@ class Coordinates:
 
         self._destination_timestamp_ns += int(total_seconds * NANOSECONDS_PER_SECOND)
 
-    def move_to(self, destination: DestinationType) -> None:
+    def move_to(
+        self,
+        destination: DestinationType,
+        tick: Optional[bool] = None,
+    ) -> None:
         self._stop()
         timestamp, self._destination_tzname = extract_timestamp_tzname(destination)
         self._destination_timestamp_ns = int(timestamp * NANOSECONDS_PER_SECOND)
         self._requested = False
         self._start()
+        if tick is not None:
+            self._tick = tick
 
     def _start(self) -> None:
         if HAVE_TZSET and self._destination_tzname is not None:
@@ -421,13 +427,19 @@ if pytest is not None:  # pragma: no branch
             self.traveller = None
             self.coordinates = None
 
-        def move_to(self, destination: DestinationType) -> None:
+        def move_to(
+            self,
+            destination: DestinationType,
+            tick: Optional[bool] = None,
+        ) -> None:
             if self.traveller is None:
-                self.traveller = travel(destination)
+                if tick is None:
+                    tick = True
+                self.traveller = travel(destination, tick=tick)
                 self.coordinates = self.traveller.start()
             else:
                 assert self.coordinates is not None
-                self.coordinates.move_to(destination)
+                self.coordinates.move_to(destination, tick=tick)
 
         def stop(self) -> None:
             if self.traveller is not None:


Test patch:
diff --git a/tests/test_time_machine.py b/tests/test_time_machine.py
index ccf3862..175a7d5 100644
--- a/tests/test_time_machine.py
+++ b/tests/test_time_machine.py
@@ -633,6 +633,20 @@ def test_move_to_datetime_with_tzinfo_zoneinfo():
     assert time.daylight == orig_daylight
 
 
+def test_move_to_datetime_change_tick_on():
+    with time_machine.travel(EPOCH, tick=False) as traveller:
+        traveller.move_to(EPOCH_PLUS_ONE_YEAR_DATETIME, tick=True)
+        assert time.time() == EPOCH_PLUS_ONE_YEAR
+        assert time.time() > EPOCH_PLUS_ONE_YEAR
+
+
+def test_move_to_datetime_change_tick_off():
+    with time_machine.travel(EPOCH, tick=True) as traveller:
+        traveller.move_to(EPOCH_PLUS_ONE_YEAR_DATETIME, tick=False)
+        assert time.time() == EPOCH_PLUS_ONE_YEAR
+        assert time.time() == EPOCH_PLUS_ONE_YEAR
+
+
 # uuid tests
 
 
@@ -664,6 +678,19 @@ def test_fixture_used(time_machine):
     assert time.time() == EPOCH
 
 
+def test_fixture_used_tick_false(time_machine):
+    time_machine.move_to(EPOCH, tick=False)
+    assert time.time() == EPOCH
+    assert time.time() == EPOCH
+
+
+def test_fixture_used_tick_true(time_machine):
+    time_machine.move_to(EPOCH, tick=True)
+    original = time.time()
+    assert original == EPOCH
+    assert original < time.time() < EPOCH + 10.0
+
+
 def test_fixture_used_twice(time_machine):
     time_machine.move_to(EPOCH)
     assert time.time() == EPOCH