Repository: adamchainz/time-machine

Issue:
Segmentation fault in `time_machine_travel(timedelta())`
### Python Version

3.12

### pytest Version

_No response_

### Package Version

2.14.0

### Description

The following Python script:

```py
from datetime import timedelta
import time_machine

with time_machine.travel(timedelta()):
    pass
```

crashes due to a segmentation fault in the constructor of the `travel` class.
    
The crash happens when the constructor tries to determine the destination timestamp like this:

```py    
    timestamp = time() + dest.total_seconds()
```    
where `time()` calls `result = _time_machine.original_time()` which crashes if time_machine's patches are not applied yet and thus the `original_time` function pointer is still `NULL`.

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d83e48d..5984577 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,6 +2,10 @@
 Changelog
 =========
 
+* Fix segmentation fault when the first ``travel()`` call in a process uses a ``timedelta``.
+
+  Thanks to Marcin Sulikowski for the report in `Issue #431 <https://github.com/adamchainz/time-machine/issues/431>`__.
+
 2.14.0 (2024-03-03)
 -------------------
 
diff --git a/src/time_machine/__init__.py b/src/time_machine/__init__.py
index 877b325..2553b3d 100644
--- a/src/time_machine/__init__.py
+++ b/src/time_machine/__init__.py
@@ -5,6 +5,7 @@ import functools
 import inspect
 import os
 import sys
+import time as time_module
 import uuid
 from collections.abc import Generator
 from time import gmtime as orig_gmtime
@@ -126,7 +127,7 @@ def extract_timestamp_tzname(
             dest = dest.replace(tzinfo=dt.timezone.utc)
         timestamp = dest.timestamp()
     elif isinstance(dest, dt.timedelta):
-        timestamp = time() + dest.total_seconds()
+        timestamp = time_module.time() + dest.total_seconds()
     elif isinstance(dest, dt.date):
         timestamp = dt.datetime.combine(
             dest, dt.time(0, 0), tzinfo=dt.timezone.utc


Test patch:
diff --git a/tests/test_time_machine.py b/tests/test_time_machine.py
index 11dc433..5557148 100644
--- a/tests/test_time_machine.py
+++ b/tests/test_time_machine.py
@@ -3,6 +3,7 @@ from __future__ import annotations
 import asyncio
 import datetime as dt
 import os
+import subprocess
 import sys
 import time
 import typing
@@ -10,6 +11,7 @@ import uuid
 from contextlib import contextmanager
 from importlib.util import module_from_spec
 from importlib.util import spec_from_file_location
+from textwrap import dedent
 from unittest import SkipTest
 from unittest import TestCase
 from unittest import mock
@@ -472,6 +474,25 @@ def test_destination_timedelta():
         assert now + 3600 <= time.time() <= now + 3601
 
 
+def test_destination_timedelta_first_travel_in_process():
+    # Would previously segfault
+    subprocess.run(
+        [
+            sys.executable,
+            "-c",
+            dedent(
+                """
+                from datetime import timedelta
+                import time_machine
+                with time_machine.travel(timedelta()):
+                    pass
+                """
+            ),
+        ],
+        check=True,
+    )
+
+
 def test_destination_timedelta_negative():
     now = time.time()
     with time_machine.travel(dt.timedelta(seconds=-3600)):