time-machine icon indicating copy to clipboard operation
time-machine copied to clipboard

Add support for `monotonic_ns`

Open patrick91 opened this issue 4 years ago • 2 comments

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 😊

patrick91 avatar Jan 21 '21 11:01 patrick91

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 with time.CLOCK_MONOTONIC or time.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, real time.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

adamchainz avatar Jan 21 '21 11:01 adamchainz

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.

adamchainz avatar Jan 21 '21 11:01 adamchainz

Closed due to age and lack of further requests, as per comment on PR https://github.com/adamchainz/time-machine/pull/104#issuecomment-1232276789

adamchainz avatar Aug 30 '22 23:08 adamchainz