Source code for timeutils.stopwatch

"""Stopwatch"""
# Author: Michal Ciesielczyk
# Licence: MIT
import time
import sys

from .timespan import TimeSpan


[docs]class Stopwatch: """Provides a set of methods and properties that you can use to accurately measure elapsed time. :param start: if set to `True`, immediately starts measuring the time (by calling :py:meth:`~.stopwatch.Stopwatch.start`). By default set to `False`. :type start: bool, optional :param verbose: if set to `True`, logs the elapsed time when the :py:meth:`~.stopwatch.Stopwatch.stop` method is called. By default set to `False`. :type verbose: bool, optional :param label: Optional stopwatch label to be included in the log messages (only if `verbose` is `True`). :type label: str, optional :param logger: logger object for logging stopwatch messages if `verbose` is `True`. If set to `None` (the default), the logger is set to :py:obj:`python:sys.stdout`. :type logger: :py:class:`python:logging.Logger`, optional :param logger_level: logging level as defined in the build-in `logging <https://docs.python.org/3/library/logging.html#logging-levels>`_ package (only if the `logger` object is set). :type logger_level: int, optional .. rubric:: Examples Simple time measurement:: sw = Stopwatch(start=True) # code to be measured sw.stop() Getting the elapsed time:: print(sw.elapsed) # hh:mm:ss.ms print(sw.elapsed.human_str()) # human-readable time Restarting the stopwatch instance:: sw.restart() Pausing and resuming the stopwatch:: sw.suspend() # code block not included in the measurement sw.resume() Using a logger:: import logging logger = logging.getLogger() logger.setLevel(logging.INFO) logger.addHandler(logging.FileHandler(filename='example.log')) sw = Stopwatch(verbose=True, label='Example', logger=logger) sw.start() # code to be measured sw.stop() .. note:: :py:class:`~.stopwatch.Stopwatch` methods are protected against inappropriate calls. It is an error to start or stop a :py:class:`~.stopwatch.Stopwatch` object that is already in the desired state. .. seealso:: Documentation of the :py:class:`~.timespan.TimeSpan` class. """ def __init__(self, start=False, verbose=False, label=None, logger=None, logger_level=None): """Creates a new :py:class:`~.stopwatch.Stopwatch` instance.""" self._label = "Stopwatch" if label is None else label self.__elapsed = 0 self.__stop_time = None if verbose: if logger is None: log = sys.stdout.write elif logger_level is None: log = logger.info else: def log(*args, **kwargs): logger.log(logger_level, *args, **kwargs) else: def log(*args, **kwargs): pass # do-nothing function self.__log = log self.__start_time = time.time() if start else None self.__last_resume = self.__start_time @property def is_running(self): """Indicates whether the :py:class:`~.stopwatch.Stopwatch` instance is running. """ return self.__start_time is not None and self.__stop_time is None
[docs] def start(self): """Starts measuring elapsed time for an interval.""" assert not self.is_running, "Stopwatch is already running." assert self.__elapsed == 0, "Stopwatch has already been executed." self.__start_time = time.time() self.__last_resume = self.__start_time
[docs] def reset(self): """Stops time interval measurement and resets the :py:class:`~.stopwatch.Stopwatch` instance. The time elapsed before reset is set to zero. """ self.__elapsed = 0 self.__start_time = None self.__stop_time = None self.__last_resume = None
@property def is_suspended(self): """Indicates whether the :py:class:`~.stopwatch.Stopwatch` instance is suspended. """ return self.__last_resume is None
[docs] def suspend(self): """Pauses the time measurement until the :py:class:`~.stopwatch.Stopwatch` instance is resumed or stopped. Returns the total elapsed time measured by the current instance. Call :py:meth:`~.stopwatch.Stopwatch.resume` to resume measuring elapsed time. """ assert self.is_running, "Stopwatch is already stopped." assert not self.is_suspended, "Stopwatch is already suspended." self.__elapsed += time.time() - self.__last_resume self.__last_resume = None return self.elapsed
[docs] def resume(self): """Resumes measuring elapsed time after calling :py:meth:`~timeutils.stopwatch.Stopwatch.suspend`. """ assert self.is_running, "Stopwatch is already stopped." assert self.is_suspended, "Stopwatch is not suspended." self.__last_resume = time.time()
[docs] def stop(self): """Stops the time measurement. Returns the total elapsed time measured by the current instance. """ assert self.is_running, "Stopwatch is already stopped." self.__stop_time = time.time() if not self.is_suspended: self.__elapsed += self.__stop_time - self.__last_resume self.__last_resume = None self.__log("{} - elapsed time: {}".format(self._label, self.elapsed)) return self.elapsed
[docs] def restart(self): """Stops time interval measurement, resets the :py:class:`~.stopwatch.Stopwatch` instance, and starts measuring elapsed time. The time elapsed before restart is set to zero. """ self.stop() self.reset() self.start()
@property def elapsed_seconds(self): """The total elapsed time in fractions of a second measured by the current instance. """ if self.is_running and not self.is_suspended: return self.__elapsed + time.time() - self.__last_resume else: return self.__elapsed @property def elapsed(self): """The total elapsed time measured by the current instance.""" return TimeSpan(seconds=self.elapsed_seconds) def __float__(self): return self.elapsed_seconds @property def start_time(self): """The time at which the time measurement has been started.""" assert self.__start_time is not None, \ "The Stopwatch hasn't been started." return self.__start_time @property def stop_time(self): """The time at which the time measurement has been stopped.""" assert self.__stop_time is not None, \ "Stopwatch hasn't been stopped yet." return self.__stop_time def __str__(self): return str(self.elapsed) def __enter__(self): self.start() def __exit__(self, exception_type, exception_value, traceback): self.stop()