From d205a632ff5db6746bab6730320b7e8956d72abe Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 29 Jun 2024 10:15:26 +0100 Subject: [PATCH] Fix System Error on Python 3.13 and Windows (#457) Fixes #456. The relevant crash from the test log actually seems to be the initial failure: ``` AttributeError: module 'time' has no attribute 'clock_gettime_ns' The above exception was the direct cause of the following exception: ... > _time_machine.patch_if_needed() E SystemError: returned a result with an exception set ``` `PyObject_GetAttrString` fails to get `clock_gettime` and `clock_gettime_ns` on Windows because they only exist on Unix. It returns `NULL` and each time sets the `AttributeError` as the current error. `patch_if_needed` did not clear this, leading to Python's call check to raise a `SystemError` caused by the `AttributeError`, making time-machine fail to start travelling. I'm not sure which exact change in Python made this failure occur, so far it seems the `SystemError` should have been raised on older versions too. Ah well. --- CHANGELOG.rst | 4 ++++ src/_time_machine.c | 18 +++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a41353c..a75559e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,10 @@ Changelog ========= +* Fix ``SystemError`` on Python 3.13 and Windows when starting time travelling. + + Thanks to Bernát Gábor for the report in `Issue #456 `__. + 2.14.1 (2024-03-22) ------------------- diff --git a/src/_time_machine.c b/src/_time_machine.c index 55e434b..9986c87 100644 --- a/src/_time_machine.c +++ b/src/_time_machine.c @@ -423,21 +423,25 @@ _time_machine_patch_if_needed(PyObject *module, PyObject *unused) PyObject *time_module = PyImport_ImportModule("time"); - - - PyCFunctionObject *time_clock_gettime = (PyCFunctionObject *) PyObject_GetAttrString(time_module, "clock_gettime"); /* - time.clock_gettime() is not always available - e.g. on builds against old macOS = official Python.org installer + time.clock_gettime(), only available on Unix platforms. */ - if (time_clock_gettime != NULL) { + PyCFunctionObject *time_clock_gettime = (PyCFunctionObject *) PyObject_GetAttrString(time_module, "clock_gettime"); + if (time_clock_gettime == NULL) { + PyErr_Clear(); + } else { state->original_clock_gettime = time_clock_gettime->m_ml->ml_meth; time_clock_gettime->m_ml->ml_meth = _time_machine_clock_gettime; Py_DECREF(time_clock_gettime); } + /* + time.clock_gettime_ns(), only available on Unix platforms. + */ PyCFunctionObject *time_clock_gettime_ns = (PyCFunctionObject *) PyObject_GetAttrString(time_module, "clock_gettime_ns"); - if (time_clock_gettime_ns != NULL) { + if (time_clock_gettime_ns == NULL) { + PyErr_Clear(); + } else { state->original_clock_gettime_ns = time_clock_gettime_ns->m_ml->ml_meth; time_clock_gettime_ns->m_ml->ml_meth = _time_machine_clock_gettime_ns; Py_DECREF(time_clock_gettime_ns);