Source code for pyjob.stopwatch

# MIT License
#
# Copyright (c) 2017-18 Felix Simkovic
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Module to store a stopwatch class"""

__author__ = "Felix Simkovic"
__date__ = "02 Jun 2017"
__version__ = "1.0"

import datetime
import logging
import time

logger = logging.getLogger(__name__)


[docs]class Time(object): """Generic time class""" def __init__(self, index): """Instantiate a new :obj:`~pyjob.stopwatch.Lap`""" self.index = index self._start_time = 0.0 self._end_time = 0.0 def __repr__(self): return "{}(index={} time={}s)".format(self.__class__.__name__, self.index, self.time) def __add__(self, other): """Add the lap times""" return self.time + other.time def __sub__(self, other): """Subtract the lap times""" return self.time - other.time @property def time(self): """Time""" return self._end_time - self._start_time @property def time_pretty(self): """Convert (seconds) to (days, hours, minutes, seconds)""" d = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=self.time) # Leave -1 in day as we start on the first day of the year return d.day - 1, d.hour, d.minute, d.second
[docs]class Lap(Time): """Lap time""" def __init__(self, index): """Instantiate a new :obj:`~pyjob.stopwatch.Lap`""" super(Lap, self).__init__(index)
[docs]class Interval(Time): """Interval time""" def __init__(self, index): """Instantiate a new :obj:`~pyjob.stopwatch.Interval`""" super(Interval, self).__init__(index) self._laps = [] self._locked = False self._running = False self._ilap = 0 def __getitem__(self, id): """Slice the intervals""" return self._laps[id] @property def average(self): """The average lap time in ms""" if len(self._laps) < 1: logger.critical("No laps taken!") return 0.0 lap_times = [lap.time for lap in self._laps] return sum(lap_times) / float(len(lap_times)) @property def lap(self): """Take a lap snapshot""" if self._locked: logger.critical("Cannot add a lap, interval finished!") return elif not self._running: logger.critical("Cannot add a lap, interval not running!") return self._ilap += 1 lap = Lap(self._ilap) lap._start_time = self._start_time + sum([l.time for l in self._laps]) lap._end_time = time.time() self._laps += [lap] return lap @property def laps(self): """The laps""" return self._laps @property def nlaps(self): """Number of laps""" return len(self._laps) @property def time(self): """Total runtime""" if self._running: return int(round(time.time() - self._start_time)) else: return int(round(self._end_time - self._start_time))
[docs] def start(self): """Start the interval""" if self._running: logger.warning("Interval is running ...") elif self._locked: logger.warning("Interval is locked ...") else: logger.debug("Starting new interval ...") self._start_time = time.time() self._running = True
[docs] def stop(self): """Stop the interval""" if self._running: logger.debug("Stopping interval ...") self._end_time = time.time() self._running = False self._locked = True else: logger.warning("Interval not running!")
[docs]class StopWatch(Time): """Stopwatch class""" def __init__(self): """Instantiate a new :obj:`~pyjob.stopwatch.StopWatch`""" super(StopWatch, self).__init__(1) self.reset() def __enter__(self): """Contextmanager entry function Note ---- For further details see `PEP 343 <https://www.python.org/dev/peps/pep-0343/>`_. """ self.start() return self def __exit__(self, *exc): """Contextmanager exit function Note ---- For further details see `PEP 343 <https://www.python.org/dev/peps/pep-0343/>`_. """ self.stop() def __getitem__(self, id): """Slice the intervals""" return self._intervals[id] def __repr__(self): return "{}(time={}s intervals={})".format(self.__class__.__name__, self.time, len(self._intervals)) @property def intervals(self): """The intervals taken""" return self._intervals @property def lap(self): """Take a lap snapshot""" if len(self._intervals) > 0 and self._intervals[-1]._running: return self._intervals[-1].lap else: logger.critical("Cannot add a lap, stopwatch not running!") @property def nintervals(self): """Number of intervals""" return len(self._intervals) @property def running(self): """Stopwatch status""" return len(self._intervals) > 0 and self._intervals[-1]._running @property def time(self): """Time in seconds""" return sum([interval.time for interval in self._intervals])
[docs] def reset(self): """Reset the timer""" self._intervals = [] self._running = False self._iinterval = 0
[docs] def start(self): """Start the interval""" if len(self._intervals) > 0 and self._intervals[-1]._running: logger.warning("Stopwatch already running!") else: logger.debug("Starting stopwatch ...") self._iinterval += 1 interval = Interval(self._iinterval) interval.start() self._intervals += [interval] return interval
[docs] def stop(self): """Stop the interval""" if len(self._intervals) > 0 and self._intervals[-1]._running: logger.debug("Stopping stopwatch ...") self._intervals[-1].stop() else: logger.warning("Stopwatch not running!") return self._intervals[-1]