time-machine
time-machine copied to clipboard
Add support for `monotonic_ns`
Hi @adamchainz 👋
would be possible to get support for time.monotonic_ns
? I can try to send a PR if you give me some pointers, I'm not really familiar with c 😊
Currently time-machine does not touch the monotonic functions, since the values they return have no relation to real time. For most use cases, using mock.patch
should be sufficient.
I think it would be okay to add support. We'd want to add support all at once for:
-
time.monotonic()
-
time.monotonic_ns()
-
time.clock_gettime()
when called withtime.CLOCK_MONOTONIC
ortime.CLOCK_MONOTONIC_RAW
Because monotonic time is a meaningless number, perhaps we could always reset to 0, or some small number, when calling travel()
, and then calls to move_to()
/ shift()
can adjust that value appropriately?
I can try to send a PR if you give me some pointers
Very good pun, intentional or not. The main way the C work is by replacing function pointers.
Here's a quick tour of how time-machine works.
- When you call
travel()
, the C-level idempotent patching function is called: https://github.com/adamchainz/time-machine/blob/d458cdbae32adf36ed5b9fbb3599430e2e8a9ea7/src/time_machine.py#L191 - That patching is here: https://github.com/adamchainz/time-machine/blob/d458cdbae32adf36ed5b9fbb3599430e2e8a9ea7/src/_time_machine.c#L324-L328 . It uses the Python C API to import functions from datetime and time, and then swaps their C pointers to its own wrappers
- One such wrapper for
time.time()
: https://github.com/adamchainz/time-machine/blob/d458cdbae32adf36ed5b9fbb3599430e2e8a9ea7/src/_time_machine.c#L265-L291 . The wrappers import and call the Python replacement functions. - Note also the
original_time()
function in C allows calling the original, pre-patch, realtime.time()
function: https://github.com/adamchainz/time-machine/blob/d458cdbae32adf36ed5b9fbb3599430e2e8a9ea7/src/_time_machine.c#L428 -
original_time()
is also exported in the module definition at the end of the file: https://github.com/adamchainz/time-machine/blob/d458cdbae32adf36ed5b9fbb3599430e2e8a9ea7/src/_time_machine.c#L428 - Over in Python the wrapper-called replacement function is a fork to calling the original time or the mocked time, based upon whether mocking is currently active: https://github.com/adamchainz/time-machine/blob/d458cdbae32adf36ed5b9fbb3599430e2e8a9ea7/src/time_machine.py#L323-L326
- Internal calculations are based upon nanoseconds to be precise (on Python 3.7+) https://github.com/adamchainz/time-machine/blob/d458cdbae32adf36ed5b9fbb3599430e2e8a9ea7/src/time_machine.py#L100-L115
Just had a check and it seems, at least on macOS, the monotonic clock has 0 = process start:
$ python -c 'import time; print(time.monotonic())'
0.023991945
So maybe resetting to 0 on travel()
, or maybe 1 or 2 seconds, would make sense.
Closed due to age and lack of further requests, as per comment on PR https://github.com/adamchainz/time-machine/pull/104#issuecomment-1232276789