Skip to content

Commit

Permalink
Fix System Error on Python 3.13 and Windows (#457)
Browse files Browse the repository at this point in the history
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: <built-in function patch_if_needed> 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.
  • Loading branch information
adamchainz committed Jun 29, 2024
1 parent 040a757 commit d205a63
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/adamchainz/time-machine/issues/456>`__.

2.14.1 (2024-03-22)
-------------------

Expand Down
18 changes: 11 additions & 7 deletions src/_time_machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit d205a63

Please sign in to comment.