time.py 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. from dataclasses import dataclass
  2. from numbers import Real # superclass for 'int' and 'float'
  3. from sccd.util.duration import *
  4. # The only requirement for a TimeImplementation is that the diffs between get_time call results are real durations of a non-complex number type (int, float).
  5. @dataclass
  6. class TimeImplementation:
  7. time_unit: Duration
  8. get_time: Callable[[], Real]
  9. # This is the most accurate time function in Python 3.6
  10. from time import perf_counter
  11. PerfCounterTime = TimeImplementation(
  12. time_unit=duration(1, Second),
  13. get_time=perf_counter) # returns float
  14. DefaultTimeImplementation = PerfCounterTime
  15. # Python >= 3.7 has a better time function
  16. import sys
  17. if sys.version_info.minor >= 7:
  18. from time import perf_counter_ns
  19. PerfCounterNSTime = TimeImplementation(
  20. time_unit=duration(1, Nanosecond),
  21. get_time=perf_counter_ns) # returns int
  22. DefaultTimeImplementation = PerfCounterNSTime
  23. # A simple "chrono" timer, using a configurable time function to measure wall-clock time passed since its start,
  24. # returning elapsed times in a configurable fixed unit, efficiently.
  25. class Timer:
  26. def __init__(self, impl: TimeImplementation, unit: Duration):
  27. self.impl = impl
  28. self.unit = unit
  29. self.paused_at = 0
  30. self.started_at = None
  31. self.convert = lambda x: int(get_conversion_f(
  32. from_unit=self.impl.time_unit, to_unit=unit)(x))
  33. self.paused = True
  34. # Start (if not paused) or continue timer (if paused)
  35. def start(self):
  36. self.started_at = self.convert(self.impl.get_time()) + self.paused_at
  37. self.paused = False
  38. def pause(self):
  39. self.paused_at = self.now()
  40. self.paused = True
  41. # The number returned will be the wall-clock time elapsed since the call to start(),
  42. # divided by the 'unit' passed to the constructor of this object, minus of course
  43. # the time elapsed while the timer was 'paused'.
  44. def now(self) -> int:
  45. assert not self.paused
  46. return self.convert(self.impl.get_time()) - self.started_at
  47. def is_paused(self) -> bool:
  48. return self.paused
  49. # Note: We could add a reset() method, but we simply don't need it for our purposes :)