|
10 | 10 |
|
11 | 11 | import dataclasses |
12 | 12 | from datetime import datetime |
| 13 | +from datetime import timezone |
13 | 14 | from time import perf_counter |
14 | 15 | from time import sleep |
15 | 16 | from time import time |
|
20 | 21 | from pytest import MonkeyPatch |
21 | 22 |
|
22 | 23 |
|
| 24 | +@dataclasses.dataclass(frozen=True) |
23 | 25 | class Instant: |
24 | 26 | """ |
25 | | - Provides a measurement of timing between different points in the code. |
26 | | -
|
27 | | - Useful to compute the elapsed time between two points in time, |
28 | | - using a performance counter, and the beginning/end in actual times (as seconds since epoch). |
| 27 | + Measures timing between different points in the code. |
29 | 28 |
|
30 | 29 | Inspired by Rust's `std::time::Instant`. |
31 | 30 | """ |
32 | 31 |
|
33 | | - def __init__(self) -> None: |
34 | | - import _pytest.timing # noqa: PLW0406 |
35 | | - |
36 | | - self._start_perf = _pytest.timing.perf_counter() |
37 | | - self._start_time = _pytest.timing.time() |
38 | | - |
39 | | - def elapsed_s(self) -> float: |
40 | | - """Return the elapsed time (in seconds) since this Instant was created, using a precise clock.""" |
41 | | - import _pytest.timing # noqa: PLW0406 |
42 | | - |
43 | | - return _pytest.timing.perf_counter() - self._start_perf |
44 | | - |
45 | | - def interval(self) -> tuple[float, float]: |
46 | | - """Return the beginning and end times of this instant, in seconds since epoch, as provided by time.time().""" |
47 | | - import _pytest.timing # noqa: PLW0406 |
48 | | - |
49 | | - return self._start_time, _pytest.timing.time() |
50 | | - |
51 | | - def __repr__(self) -> str: # pragma: no cover |
52 | | - return f"Instant(start_time={self._start_time}, elapsed_s={self.elapsed_s()})" |
| 32 | + # Creation time of this instant, using time.time(), to measure actual time. |
| 33 | + # Use a `lambda` to initialize the default to correctly get the mocked time via `MockTiming`. |
| 34 | + _start_time: float = dataclasses.field(default_factory=lambda: time()) |
| 35 | + |
| 36 | + # Initial "tick" of the performance counter to measure precise elapsed time. |
| 37 | + # Use a `lambda` to initialize the default to correctly get the mocked time via `MockTiming`. |
| 38 | + _start_perf: float = dataclasses.field(default_factory=lambda: perf_counter()) |
| 39 | + |
| 40 | + def duration(self) -> Duration: |
| 41 | + """Measure the duration since `Instant` was created.""" |
| 42 | + return Duration( |
| 43 | + start=self._start_time, |
| 44 | + elapsed_s=perf_counter() - self._start_perf, |
| 45 | + stop=time(), |
| 46 | + ) |
| 47 | + |
| 48 | + |
| 49 | +@dataclasses.dataclass(frozen=True) |
| 50 | +class Duration: |
| 51 | + """A slice of time as measured by `Instant.duration()`.""" |
| 52 | + |
| 53 | + # Start time of the duration, as seconds since epoch. |
| 54 | + start: float |
| 55 | + # Stop time of the duration, as seconds since epoch. |
| 56 | + stop: float |
| 57 | + # Elapsed time of the duration, in seconds, measured using a performance counter for precise timing. |
| 58 | + elapsed_s: float |
| 59 | + |
| 60 | + def start_utc(self) -> datetime: |
| 61 | + """Start time of the duration, as a datetime in UTC.""" |
| 62 | + return datetime.fromtimestamp(self.start, timezone.utc) |
| 63 | + |
| 64 | + def stop_utc(self) -> datetime: # pragma: no cover |
| 65 | + """Stop time of the duration, as a datetime in UTC.""" |
| 66 | + return datetime.fromtimestamp(self.stop, timezone.utc) |
53 | 67 |
|
54 | 68 |
|
55 | 69 | @dataclasses.dataclass |
|
0 commit comments