From f0363026bbed4043f5672bc0b79a58d2d1c627ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl> Date: Wed, 12 Feb 2020 19:19:22 +0100 Subject: [PATCH] add satella.time.measure --- CHANGELOG.md | 1 + docs/index.rst | 1 + docs/time.rst | 7 +++++ satella/__init__.py | 2 +- satella/time.py | 67 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_time.py | 20 ++++++++++++++ 6 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 docs/time.rst create mode 100644 satella/time.py create mode 100644 tests/test_time.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f3985a72..8fb8d9e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * changed the behaviour of `JSONEncoder` when it comes to serializing unknown objects * moved the exception `LockIsHeld` to it's proper place * fixed `dump_frames_on` not to log on **sys.stderr** +* added `satella.time.measure` # v2.4.16 diff --git a/docs/index.rst b/docs/index.rst index 2608553a..7fa8dfbe 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,6 +23,7 @@ Visit the project's page at GitHub_! posix import files + time Indices and tables diff --git a/docs/time.rst b/docs/time.rst new file mode 100644 index 00000000..dbec771d --- /dev/null +++ b/docs/time.rst @@ -0,0 +1,7 @@ +time +==== + +Sometimes you just need to measure how long does a routine call take. + +.. autoclass:: satella.time.measure + :members: diff --git a/satella/__init__.py b/satella/__init__.py index 2f1aa7da..2e27e304 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.4.17a9' +__version__ = '2.4.17' diff --git a/satella/time.py b/satella/time.py new file mode 100644 index 00000000..1db71846 --- /dev/null +++ b/satella/time.py @@ -0,0 +1,67 @@ +import logging +import typing as tp +import time +import functools + +logger = logging.getLogger(__name__) + +__all__ = ['measure'] + + +class measure: + """ + A class used to measure time elapsed. Use for example like this: + + >>> with measure() as measurement: + >>> time.sleep(1) + >>> print('This has taken so far', measurement(), 'seconds') + >>> time.sleep(1) + >>> print('A total of ', measurement(), 'seconds have elapsed') + + You can also use the .start() method instead of context manager. Time measurement + will stop after exiting or calling .stop() depending on stop_on_stop flag. + + You can also decorate your functions to have them keep track time of their execution, like that: + + >>> @measure() + >>> def measuring(measurement_object: measure, *args, **kwargs): + >>> ... + + :param stop_on_stop: stop elapsing time upon calling .stop()/exiting the context manager + """ + def __init__(self, stop_on_stop: bool = True): + self.started_on = None + self.elapsed = None + self.stopped_on = None + self.stop_on_stop = stop_on_stop + + def start(self) -> None: + """Start measuring time""" + self.started_on = time.monotonic() + + def __call__(self, fun: tp.Optional[tp.Callable] = None) -> float: + if fun is None: + if self.started_on is None: + raise RuntimeError('Time measurement did not start yet, use .start()') + if self.stop_on_stop and self.elapsed is not None: + return self.elapsed + return time.monotonic() - self.started_on + else: + @functools.wraps(fun) + def inner(*args, **kwargs): + with self: + return fun(self, *args, **kwargs) + return inner + + def __enter__(self): + self.start() + return self + + def stop(self) -> None: + """Stop counting time""" + self.stopped_on = time.monotonic() + self.elapsed = self.stopped_on - self.started_on + + def __exit__(self, exc_type, exc_val, exc_tb): + self.stop() + return False diff --git a/tests/test_time.py b/tests/test_time.py new file mode 100644 index 00000000..5389cc71 --- /dev/null +++ b/tests/test_time.py @@ -0,0 +1,20 @@ +import unittest +import typing as tp +import time +from satella.time import measure + + +class TestTime(unittest.TestCase): + def test_measure(self): + with measure() as measurement: + time.sleep(0.5) + + self.assertGreaterEqual(measurement(), 0.5) + + def test_measure_decorator(self): + @measure() + def measured(measurement): + time.sleep(0.5) + self.assertGreaterEqual(measurement(), 0.5) + measured() + -- GitLab