Source code for ampworks.utils._timer

from __future__ import annotations

import time
import textwrap


[docs] class Timer: """Timer utility.""" __slots__ = ('name', '_units', '_converter', '_start', '_stop', '_display') def __init__( self, name: str = 'Elapsed time', units: str = 's', display: bool = True, ) -> None: """ Measures elapsed time for a series of steps and prints results to the console. Initialize with a name to tell what steps each print statement is associated with when timing multiple blocks. Also has control to print in different units of time. Parameters ---------- name : str, optional Code block name used in print. The default is 'Elapsed time'. units : str, optional Printing units, from {'s', 'min', 'h'}. The default is 's'. display : bool, optional Whether to print the elapsed time when exiting a context block. The default is True. Notes ----- If you want to print in multiple units, you can call `print_elapsed()` directly after exiting a context block. If your units are not seconds, minutes, or hours, you can also perform your own conversion using the `elapsed_time` property, which always returns seconds. A timer can be reused for multiple context blocks if desired, but the `elapsed_time` property will only return the most recent elapsed time because each time you enter a context block the start time is reset. So, make sure to print or store intermediate values if you want to keep track of multiple context blocks with a single timer. Examples -------- The `Timer` works as a context manager and is accessed using `with` blocks. For example: .. code-block:: python import time from ampworks.utils import Timer def function(sleep_time: float) -> None: time.sleep(sleep_time) with Timer(): function(2.) If you want to silence the print statement and just store the elapsed time, set `display=False` and access the `elapsed_time` property: .. code-block:: python with Timer(display=False) as timer: function(2.) print(f"Elapsed time: {timer.elapsed_time:.5f} s") """ from ampworks._checks import _check_literal _check_literal('units', units, {'s', 'min', 'h'}) self.name = name self._units = units self._converter = { 's': lambda t: t, 'min': lambda t: t / 60., 'h': lambda t: t / 3600., } self._start = 0. self._stop = 0. self._display = display def __repr__(self) -> str: # pragma: no cover elapsed = self._converter[self._units](self.elapsed_time) elapsed_string = f"{elapsed} {self._units}" data = { 'name': self.name, 'elapsed': elapsed_string, } summary = "\n".join([f"{k}={v!r}," for k, v in data.items()]) summary = textwrap.indent(summary, " " * 4) return f"{self.__class__.__name__}(\n{summary}\n)" def __enter__(self) -> Timer: """Store start time when entering "with" block.""" self._start = time.perf_counter() return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: """Store stop time when exiting "with" block, and print.""" self._stop = time.perf_counter() if self._display: self.print_elapsed(self._units) @property def elapsed_time(self) -> float: """ Return the elapsed time in seconds. Returns ------- elapsed : float Time difference between entering and exiting a 'with' block. Will return zero if it has not yet been used. """ return self._stop - self._start
[docs] def print_elapsed(self, units: str = 's') -> None: """ Print the elapsed time. Parameters ---------- units : str, optional Printing units, from {'s', 'min', 'h'}. The default is 's'. """ elapsed = self._converter[units](self.elapsed_time) print(f"{self.name}: {elapsed:.5f} {units}")