Source code for chronometer
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import sys
from monotonic import monotonic
__version__ = '1.0'
__all__ = ['Chronometer',
'RelaxedStartChronometer',
'RelaxedStopChronometer',
'RelaxedChronometer',
'ChronoRuntimeError',
'ChronoAlreadyStartedError',
'ChronoAlreadyStoppedError', ]
PY2 = sys.version_info[0] == 2
[docs]class ChronoRuntimeError(Exception):
"""Base exceptions for errors which happened inside Chronometer."""
[docs]class ChronoAlreadyStoppedError(ChronoRuntimeError):
"""Raised when trying to stop a stopped timer."""
[docs]class ChronoAlreadyStartedError(ChronoRuntimeError):
"""raised when trying to start a started timer."""
[docs]class Chronometer(object):
"""Simple timer meant to be used for measuring how much time has been
spent in a certain code region.
"""
__slots__ = ('timer', 'since', 'until', )
def __init__(self, timer=monotonic):
self.since, self.until, self.timer = None, None, timer
def __enter__(self):
return self.start()
def __exit__(self, exc_type, exc_value, exc_traceback):
self.stop()
def __repr__(self):
state = 'stopped' if self.stopped else 'started'
return '<{0} {1} {2}>'.format(type(self).__name__,
state, self.elapsed)
def __format__(self, format_spec):
v = int(self.elapsed) if format_spec[-1] == 'd' else self.elapsed
return format(v, format_spec)
def __float__(self):
return self.elapsed
def __bool__(self):
return self.started
if PY2:
__nonzero__ = __bool__
def __str__(self):
return '{0:.5f}s'.format(self.elapsed)
if PY2:
__unicode__ = __str__
__str__ = lambda self: self.__unicode__().encode('utf-8')
def _set(self, since=None, until=None):
self.since, self.until = since, until
[docs] def start(self):
"""Starts the timer.
:return:
Returns the timer itself.
:rtype:
Chronometer
:raises TimerAlreadyStartedError:
If the timer is already running.
"""
if self.started:
raise ChronoAlreadyStartedError('Timer already started.')
self._set(self.timer())
return self
[docs] def stop(self):
"""Stops the timer.
:return:
Time passed since the timer has been started in seconds.
:rtype:
float
:raises TimerAlreadyStoppedError:
If the timer is already stopped.
"""
if self.stopped:
raise ChronoAlreadyStoppedError('Timer already stopped.')
self.until = self.timer()
return self.elapsed
[docs] def reset(self):
"""Resets the timer.
:return:
Elapsed time before the timer was reset.
:rtype:
float
"""
try:
return self.elapsed
finally:
self._set(None if self.stopped else self.timer())
@property
[docs] def elapsed(self):
"""Returns time passed in seconds.
:return:
Time passed since the timer has been started in seconds.
:rtype:
float
"""
pit = self.timer()
return (self.until or pit) - (self.since or pit)
@property
[docs] def stopped(self):
"""Returns if the timer is stopped or not.
:return:
`True` if the timer is stopped and `False` otherwise.
:rtype:
bool
"""
return self.since is None or self.until is not None
@property
[docs] def started(self):
"""Returns if the timer is running or not.
:return:
`True` if the timer is running and `False` otherwise.
:rtype:
bool
"""
return not self.stopped
[docs]class RelaxedStartChronometer(Chronometer):
"""Relaxed version which won't raise an exception on double starting
the timer.
"""
__slots__ = []
[docs] def start(self):
"""Starts the timer or just returns if the timer is already running.
:return:
Returns the timer itself.
:rtype:
RelaxedStartChronometer
"""
try:
return super(RelaxedStartChronometer, self).start()
except ChronoAlreadyStartedError:
return self
[docs]class RelaxedStopChronometer(Chronometer):
"""Relaxed version which won't raise an exception on double stopping
the timer.
"""
__slots__ = []
[docs] def stop(self):
"""Stops the timer or just returns if the timer is already stopped.
:return:
Time passed since the timer has been started in seconds.
:rtype:
float
"""
try:
return super(RelaxedStopChronometer, self).stop()
except ChronoAlreadyStoppedError:
return self.elapsed
[docs]class RelaxedChronometer(RelaxedStartChronometer, RelaxedStopChronometer):
"""Ultra relaxed version which won't throw any exceptions on its own.
"""
__slots__ = []