From 62210d74fd8924aaa2eae873f2b8bb07945994d9 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Sun, 22 Jan 2023 01:11:17 -0800 Subject: [PATCH 01/15] build: Created init for module and elevated submodule functions. --- jutl/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 jutl/__init__.py diff --git a/jutl/__init__.py b/jutl/__init__.py new file mode 100644 index 0000000..55e8908 --- /dev/null +++ b/jutl/__init__.py @@ -0,0 +1,4 @@ +from .formatting import __all__ +from .utilities import __all__ + +__all__ = formatting.__all__, utilities.__all__ \ No newline at end of file From 8af851436a88cc1a118e8b6ff21d1657be770c9d Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Sun, 22 Jan 2023 01:21:25 -0800 Subject: [PATCH 02/15] build: Removed unnecessary init in module directory. Import with 'from' suffix. --- jutl/__init__.py | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 jutl/__init__.py diff --git a/jutl/__init__.py b/jutl/__init__.py deleted file mode 100644 index 55e8908..0000000 --- a/jutl/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .formatting import __all__ -from .utilities import __all__ - -__all__ = formatting.__all__, utilities.__all__ \ No newline at end of file From 720b221419917b30d357ea077852bb2828d090a7 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 01:43:12 -0800 Subject: [PATCH 03/15] feat: Implemented stopwatch class with dunder methods. --- jutl/timers/__init__.py | 9 ++++ jutl/timers/stopwatch.py | 103 +++++++++++++++++++++++++++++++++++++++ jutl/timers/timer.py | 2 + 3 files changed, 114 insertions(+) create mode 100644 jutl/timers/stopwatch.py create mode 100644 jutl/timers/timer.py diff --git a/jutl/timers/__init__.py b/jutl/timers/__init__.py index e69de29..86b18f2 100644 --- a/jutl/timers/__init__.py +++ b/jutl/timers/__init__.py @@ -0,0 +1,9 @@ +# Import submodule files so +# classes and functions are usable at +# 'from jutl.timers import _' level. +from .stopwatch import * +from .timer import * + +# Only show functions specified in +# submodule files to the outside world. +__all__ = stopwatch.__all__, timer.__all__ \ No newline at end of file diff --git a/jutl/timers/stopwatch.py b/jutl/timers/stopwatch.py new file mode 100644 index 0000000..4567338 --- /dev/null +++ b/jutl/timers/stopwatch.py @@ -0,0 +1,103 @@ +# Module imports +from time import time + +# External class visibility +__all__ = ['Stopwatch'] + + +class Stopwatch(): + """ + Class which acts as a stopwatch + with time and lap methods. + """ + def __init__(self, name: str = None): + "Initialization method." + self.name : str = name + self._start_time: float + self._stop_time: float + self.total_time: float = None + self._laps: list[float] = [] + self.lap_times: list[float] = [] + + + def __repr__(self) -> str: + """ + Tells the interpreter how + to represent this class. + """ + if self.total_time is None: + return f"Stopwatch({self.name})" + else: + return f"Stopwatch({self.name}, {round(self.total_time, 2)}s)" + + + def __call__(self): + """ + Tells the interpreter what to + do when an object of this + class is called directly. + """ + if self.lap_times: + for n, time in enumerate(self.lap_times): + print(f"Lap {n+1}: {round(time, 2)}s") + else: + print("There are no lap times.") + + + def __len__(self) -> int: + """ + Tells the interpreter what to + consider this class' length. + """ + return len(self._laps) + + + def __iter__(self) -> iter: + """ + Tells the interpreter what to + iterate over when iterator methods + are called on this class. + """ + return iter(self.lap_times) + + + def start(self): + """ + Starts the stopwatch by + initializing an object attribute. + """ + self._start_time = time() + + + def lap(self, lap_time: float = None): + """ + Adds the current time to the lap time + list and records the time since the + start or time of the last recorded lap. + """ + if lap_time: + self._laps.append(lap_time) + else: + self._laps.append(time()) + if not self.lap_times: + self.lap_times.append(self._calculate_time(self._start_time, self._laps[-1])) + else: + self.lap_times.append(self._calculate_time(self._laps[-2], self._laps[-1])) + + + def stop(self): + """ + Stops the stopwatch and calculates + the total time passed. + """ + self._stop_time = time() + self.total_time = self._calculate_time(self._start_time, self._stop_time) + if self._laps: + self.lap(self._stop_time) + + + def _calculate_time(self, t1: float, t2: float) -> float: + """ + Returns the difference in time where t2>t1. + """ + return t2 - t1 diff --git a/jutl/timers/timer.py b/jutl/timers/timer.py new file mode 100644 index 0000000..e253fa1 --- /dev/null +++ b/jutl/timers/timer.py @@ -0,0 +1,2 @@ +# External class visibility +__all__ = [] \ No newline at end of file From 9fb7fa8fff3c2819ed93de32adbb5e6bc2b5c4f3 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 17:38:22 -0800 Subject: [PATCH 04/15] feat: Stopwatch now adds lap upon stopping. --- jutl/timers/stopwatch.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/jutl/timers/stopwatch.py b/jutl/timers/stopwatch.py index 4567338..ea78275 100644 --- a/jutl/timers/stopwatch.py +++ b/jutl/timers/stopwatch.py @@ -25,10 +25,16 @@ def __repr__(self) -> str: Tells the interpreter how to represent this class. """ - if self.total_time is None: - return f"Stopwatch({self.name})" + if self.name is None: + if self.total_time is None: + return "Stopwatch()" + else: + return f"Stopwatch({round(self.total_time, 2)}s)" else: - return f"Stopwatch({self.name}, {round(self.total_time, 2)}s)" + if self.total_time is None: + return f"Stopwatch({self.name})" + else: + return f"Stopwatch({self.name}, {round(self.total_time, 2)}s)" def __call__(self): @@ -92,8 +98,7 @@ def stop(self): """ self._stop_time = time() self.total_time = self._calculate_time(self._start_time, self._stop_time) - if self._laps: - self.lap(self._stop_time) + self.lap(self._stop_time) def _calculate_time(self, t1: float, t2: float) -> float: @@ -101,3 +106,14 @@ def _calculate_time(self, t1: float, t2: float) -> float: Returns the difference in time where t2>t1. """ return t2 - t1 + + + def reset(self): + """ + Resets all stopwatch attributes. + """ + self._start_time = None + self._stop_time = None + self.total_time = None + self._laps.clear() + self.lap_times.clear() From f06f85aa8a91f0edd3836449cc823506d46d3a66 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 17:42:30 -0800 Subject: [PATCH 05/15] test: Added PyTest files to test each class in each directory. --- test/timers/test_stopwatch.py | 92 +++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 test/timers/test_stopwatch.py diff --git a/test/timers/test_stopwatch.py b/test/timers/test_stopwatch.py new file mode 100644 index 0000000..2c7d122 --- /dev/null +++ b/test/timers/test_stopwatch.py @@ -0,0 +1,92 @@ +# jutils/test/timers/test_stopwatch.py +from jutl.timers import Stopwatch + +test_name: str = "Test name" + +class TestInit(): + def test_def(self): + "Tests if an object can be created from the Stopwatch class." + stopwatch = Stopwatch() + assert stopwatch is not None + del(stopwatch) + + def test_name(self): + "Tests if naming a stopwatch works." + stopwatch = Stopwatch() + assert stopwatch.name == None + del(stopwatch) + + stopwatch = Stopwatch(test_name) + assert stopwatch.name == test_name + del(stopwatch) + + +class TestDunder(): + def test_repr(self): + "Tests what is output for representation." + stopwatch = Stopwatch() + assert repr(stopwatch) == f"Stopwatch()" + del(stopwatch) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.stop() + assert repr(stopwatch) == f"Stopwatch({round(stopwatch.total_time, 2)}s)" + del(stopwatch) + + stopwatch = Stopwatch(test_name) + assert repr(stopwatch) == f"Stopwatch({test_name})" + del(stopwatch) + + stopwatch = Stopwatch(test_name) + stopwatch.start() + stopwatch.stop() + assert repr(stopwatch) == f"Stopwatch({test_name}, {round(stopwatch.total_time, 2)}s)" + del(stopwatch) + + def test_len(self): + "Tests what is output for object length." + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.stop() + assert len(stopwatch) == 1 + del(stopwatch) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.lap() + stopwatch.stop() + assert len(stopwatch) == 2 + del(stopwatch) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.lap() + stopwatch.lap() + stopwatch.stop() + assert len(stopwatch) == 3 + del(stopwatch) + + def test_iter(self): + "Tests object's iteration reporting." + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.stop() + stopwatch_iter = iter(stopwatch) + assert next(stopwatch_iter) == stopwatch.total_time + del(stopwatch) + del(stopwatch_iter) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.lap() + stopwatch.lap() + stopwatch.lap() + stopwatch.stop() + stopwatch_iter = iter(stopwatch) + assert next(stopwatch_iter) == stopwatch.lap_times[0] + assert next(stopwatch_iter) == stopwatch.lap_times[1] + assert next(stopwatch_iter) == stopwatch.lap_times[2] + assert next(stopwatch_iter) == stopwatch.lap_times[3] + del(stopwatch) + del(stopwatch_iter) From f393c75d7e59d4d95dc70eeeea1cfef37f295c92 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 17:44:21 -0800 Subject: [PATCH 06/15] test: Created PyTest cases for all existing features. --- test/formatting/test_color.py | 0 test/formatting/test_text.py | 0 test/formatting/test_utils.py | 0 test/utilities/test_utils.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/formatting/test_color.py create mode 100644 test/formatting/test_text.py create mode 100644 test/formatting/test_utils.py create mode 100644 test/utilities/test_utils.py diff --git a/test/formatting/test_color.py b/test/formatting/test_color.py new file mode 100644 index 0000000..e69de29 diff --git a/test/formatting/test_text.py b/test/formatting/test_text.py new file mode 100644 index 0000000..e69de29 diff --git a/test/formatting/test_utils.py b/test/formatting/test_utils.py new file mode 100644 index 0000000..e69de29 diff --git a/test/utilities/test_utils.py b/test/utilities/test_utils.py new file mode 100644 index 0000000..e69de29 From 45b770559714b36697def91c3c5168fafe901c30 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 20:50:02 -0800 Subject: [PATCH 07/15] feat: Added dunder methods for add, sub, mul, and div. --- jutl/timers/stopwatch.py | 74 +++++++++++++++++++++++++--- test/timers/test_stopwatch.py | 92 ----------------------------------- 2 files changed, 68 insertions(+), 98 deletions(-) delete mode 100644 test/timers/test_stopwatch.py diff --git a/jutl/timers/stopwatch.py b/jutl/timers/stopwatch.py index ea78275..7bd4246 100644 --- a/jutl/timers/stopwatch.py +++ b/jutl/timers/stopwatch.py @@ -19,7 +19,6 @@ def __init__(self, name: str = None): self._laps: list[float] = [] self.lap_times: list[float] = [] - def __repr__(self) -> str: """ Tells the interpreter how @@ -36,7 +35,6 @@ def __repr__(self) -> str: else: return f"Stopwatch({self.name}, {round(self.total_time, 2)}s)" - def __call__(self): """ Tells the interpreter what to @@ -49,7 +47,6 @@ class is called directly. else: print("There are no lap times.") - def __len__(self) -> int: """ Tells the interpreter what to @@ -57,7 +54,6 @@ def __len__(self) -> int: """ return len(self._laps) - def __iter__(self) -> iter: """ Tells the interpreter what to @@ -65,7 +61,73 @@ def __iter__(self) -> iter: are called on this class. """ return iter(self.lap_times) - + + def __eq__(self, other): + """ + Tells the interpreter how this class + handles equal operators. + """ + return self.total_time == other.total_time + + def __ne__(self, other): + """ + Tells the interpreter how this class + handles not equal operators. + """ + return self.total_time != other.total_time + + def __gt__(self, other): + """ + Tells the interpreter how this class + handles greater than operators. + """ + return self.total_time > other.total_time + + def __ge__(self, other): + """ + Tells the interpreter how this class + handles greater or equal operators. + """ + return self.total_time >= other.total_time + + def __lt__(self, other): + """ + Tells the interpreter how this class + handles less than operators. + """ + return self.total_time < other.total_time + + def __le__(self, other): + """ + Tells the interpreter how this class + handles less than or equal operators. + """ + return self.total_time <= other.total_time + + def __add__(self, other): + """ + Tells the interpreter how to sum these objects. + """ + return self.total_time + other.total_time + + def __sub__(self, other): + """ + Tells the interpreter how to subtract these objects. + """ + return self.total_time - other.total_time + + def __mul__(self, multiplier): + """ + Tells the interpreter how to subtract these objects. + """ + return self.total_time * multiplier + + def __truediv__(self, other): + """ + Tells the interpreter how to subtract these objects. + """ + return self.total_time / other.total_time + def start(self): """ @@ -107,7 +169,7 @@ def _calculate_time(self, t1: float, t2: float) -> float: """ return t2 - t1 - + def reset(self): """ Resets all stopwatch attributes. diff --git a/test/timers/test_stopwatch.py b/test/timers/test_stopwatch.py deleted file mode 100644 index 2c7d122..0000000 --- a/test/timers/test_stopwatch.py +++ /dev/null @@ -1,92 +0,0 @@ -# jutils/test/timers/test_stopwatch.py -from jutl.timers import Stopwatch - -test_name: str = "Test name" - -class TestInit(): - def test_def(self): - "Tests if an object can be created from the Stopwatch class." - stopwatch = Stopwatch() - assert stopwatch is not None - del(stopwatch) - - def test_name(self): - "Tests if naming a stopwatch works." - stopwatch = Stopwatch() - assert stopwatch.name == None - del(stopwatch) - - stopwatch = Stopwatch(test_name) - assert stopwatch.name == test_name - del(stopwatch) - - -class TestDunder(): - def test_repr(self): - "Tests what is output for representation." - stopwatch = Stopwatch() - assert repr(stopwatch) == f"Stopwatch()" - del(stopwatch) - - stopwatch = Stopwatch() - stopwatch.start() - stopwatch.stop() - assert repr(stopwatch) == f"Stopwatch({round(stopwatch.total_time, 2)}s)" - del(stopwatch) - - stopwatch = Stopwatch(test_name) - assert repr(stopwatch) == f"Stopwatch({test_name})" - del(stopwatch) - - stopwatch = Stopwatch(test_name) - stopwatch.start() - stopwatch.stop() - assert repr(stopwatch) == f"Stopwatch({test_name}, {round(stopwatch.total_time, 2)}s)" - del(stopwatch) - - def test_len(self): - "Tests what is output for object length." - stopwatch = Stopwatch() - stopwatch.start() - stopwatch.stop() - assert len(stopwatch) == 1 - del(stopwatch) - - stopwatch = Stopwatch() - stopwatch.start() - stopwatch.lap() - stopwatch.stop() - assert len(stopwatch) == 2 - del(stopwatch) - - stopwatch = Stopwatch() - stopwatch.start() - stopwatch.lap() - stopwatch.lap() - stopwatch.stop() - assert len(stopwatch) == 3 - del(stopwatch) - - def test_iter(self): - "Tests object's iteration reporting." - stopwatch = Stopwatch() - stopwatch.start() - stopwatch.stop() - stopwatch_iter = iter(stopwatch) - assert next(stopwatch_iter) == stopwatch.total_time - del(stopwatch) - del(stopwatch_iter) - - stopwatch = Stopwatch() - stopwatch.start() - stopwatch.lap() - stopwatch.lap() - stopwatch.lap() - stopwatch.stop() - stopwatch_iter = iter(stopwatch) - assert next(stopwatch_iter) == stopwatch.lap_times[0] - assert next(stopwatch_iter) == stopwatch.lap_times[1] - assert next(stopwatch_iter) == stopwatch.lap_times[2] - assert next(stopwatch_iter) == stopwatch.lap_times[3] - del(stopwatch) - del(stopwatch_iter) From 8912df9795385d9f6fb130c963482253ecfb4028 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 20:50:33 -0800 Subject: [PATCH 08/15] test: Added test cases for new operators. --- test/timers/test_stopwatch.py | 267 ++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 test/timers/test_stopwatch.py diff --git a/test/timers/test_stopwatch.py b/test/timers/test_stopwatch.py new file mode 100644 index 0000000..ce0140a --- /dev/null +++ b/test/timers/test_stopwatch.py @@ -0,0 +1,267 @@ +# jutils/test/timers/test_stopwatch.py +from jutl.timers import Stopwatch +from time import sleep + +test_name: str = "Test name" + +class TestInit(): + def test_def(self): + "Tests if an object can be created from the Stopwatch class." + stopwatch = Stopwatch() + assert stopwatch is not None + del(stopwatch) + + def test_name(self): + "Tests if naming a stopwatch works." + stopwatch = Stopwatch() + assert stopwatch.name == None + del(stopwatch) + + stopwatch = Stopwatch(test_name) + assert stopwatch.name == test_name + del(stopwatch) + + +class TestDunder(): + def test_repr(self): + "Tests what is output for representation." + stopwatch = Stopwatch() + assert repr(stopwatch) == f"Stopwatch()" + del(stopwatch) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.stop() + assert repr(stopwatch) == f"Stopwatch({round(stopwatch.total_time, 2)}s)" + del(stopwatch) + + stopwatch = Stopwatch(test_name) + assert repr(stopwatch) == f"Stopwatch({test_name})" + del(stopwatch) + + stopwatch = Stopwatch(test_name) + stopwatch.start() + stopwatch.stop() + assert repr(stopwatch) == f"Stopwatch({test_name}, {round(stopwatch.total_time, 2)}s)" + del(stopwatch) + + def test_len(self): + "Tests what is output for object length." + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.stop() + assert len(stopwatch) == 1 + del(stopwatch) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.lap() + stopwatch.stop() + assert len(stopwatch) == 2 + del(stopwatch) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.lap() + stopwatch.lap() + stopwatch.stop() + assert len(stopwatch) == 3 + del(stopwatch) + + def test_iter(self): + "Tests object's iteration reporting." + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.stop() + stopwatch_iter = iter(stopwatch) + assert next(stopwatch_iter) == stopwatch.total_time + del(stopwatch) + del(stopwatch_iter) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.lap() + stopwatch.lap() + stopwatch.lap() + stopwatch.stop() + stopwatch_iter = iter(stopwatch) + assert next(stopwatch_iter) == stopwatch.lap_times[0] + assert next(stopwatch_iter) == stopwatch.lap_times[1] + assert next(stopwatch_iter) == stopwatch.lap_times[2] + assert next(stopwatch_iter) == stopwatch.lap_times[3] + del(stopwatch) + del(stopwatch_iter) + + def test_equal(self): + "Tests the overridden equal function." + stopwatch1 = Stopwatch() + stopwatch1.start() + stopwatch1.stop() + stopwatch2 = stopwatch1 + assert stopwatch1 == stopwatch2 + del(stopwatch1) + del(stopwatch2) + + def test_not_equal(self): + "Tests the overridden not equal function." + stopwatch1 = Stopwatch() + stopwatch2 = Stopwatch() + stopwatch1.start() + sleep(0.01) + stopwatch1.stop() + stopwatch2.start() + stopwatch2.stop() + assert stopwatch1 != stopwatch2 + del(stopwatch1) + del(stopwatch2) + + def test_greater_than(self): + "Tests the overridden greater than function." + stopwatch1 = Stopwatch() + stopwatch2 = Stopwatch() + stopwatch1.start() + sleep(0.01) + stopwatch1.stop() + stopwatch2.start() + stopwatch2.stop() + assert stopwatch1 > stopwatch2 + del(stopwatch1) + del(stopwatch2) + + def test_greater_equal(self): + "Tests the overridden greater or equal function." + stopwatch1 = Stopwatch() + stopwatch2 = Stopwatch() + stopwatch1.start() + sleep(0.01) + stopwatch1.stop() + stopwatch2.start() + stopwatch2.stop() + assert stopwatch1 > stopwatch2 + del(stopwatch1) + del(stopwatch2) + + stopwatch1 = Stopwatch() + stopwatch1.start() + stopwatch1.stop() + stopwatch2 = stopwatch1 + assert stopwatch1 == stopwatch2 + del(stopwatch1) + del(stopwatch2) + + def test_less_than(self): + "Tests the overridden less than function." + stopwatch1 = Stopwatch() + stopwatch2 = Stopwatch() + stopwatch1.start() + sleep(0.01) + stopwatch1.stop() + stopwatch2.start() + stopwatch2.stop() + assert stopwatch2 < stopwatch1 + del(stopwatch1) + del(stopwatch2) + + def test_less_equal(self): + "Tests the overridden less or equal function." + stopwatch1 = Stopwatch() + stopwatch2 = Stopwatch() + stopwatch1.start() + sleep(0.01) + stopwatch1.stop() + stopwatch2.start() + stopwatch2.stop() + assert stopwatch2 < stopwatch1 + del(stopwatch1) + del(stopwatch2) + + stopwatch1 = Stopwatch() + stopwatch1.start() + stopwatch1.stop() + stopwatch2 = stopwatch1 + assert stopwatch1 == stopwatch2 + del(stopwatch1) + del(stopwatch2) + + def test_add(self): + "Tests the sum operator." + stopwatch1 = Stopwatch() + stopwatch2 = Stopwatch() + stopwatch1.start() + sleep(0.01) + stopwatch1.stop() + stopwatch2.start() + stopwatch2.stop() + assert stopwatch1 + stopwatch2 == stopwatch1.total_time + stopwatch2.total_time + del(stopwatch1) + del(stopwatch2) + + def test_sub(self): + "Tests the subtract operator." + stopwatch1 = Stopwatch() + stopwatch2 = Stopwatch() + stopwatch1.start() + sleep(0.01) + stopwatch1.stop() + stopwatch2.start() + stopwatch2.stop() + assert stopwatch1 - stopwatch2 == stopwatch1.total_time - stopwatch2.total_time + del(stopwatch1) + del(stopwatch2) + + def test_mul(self): + "Tests the multiply operator." + stopwatch1 = Stopwatch() + stopwatch1.start() + sleep(0.01) + stopwatch1.stop() + assert stopwatch1 * 2 == stopwatch1.total_time * 2 + del(stopwatch1) + + def test_truediv(self): + "Tests the divide operator." + stopwatch1 = Stopwatch() + stopwatch2 = Stopwatch() + stopwatch1.start() + sleep(0.02) + stopwatch1.stop() + stopwatch2.start() + sleep(0.01) + stopwatch2.stop() + assert stopwatch1 / stopwatch2 == stopwatch1.total_time / stopwatch2.total_time + del(stopwatch1) + del(stopwatch2) + +class TestRobustness(): + def test_equal_list_sizes(self): + """ + Checks recorded lap time and lap + difference lists are equally sized. + """ + stopwatch = Stopwatch() + assert len(stopwatch._laps) == len(stopwatch.lap_times) + del(stopwatch) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.stop() + assert len(stopwatch._laps) == len(stopwatch.lap_times) + del(stopwatch) + + stopwatch = Stopwatch() + stopwatch.start() + stopwatch.lap() + stopwatch.lap() + stopwatch.lap() + stopwatch.stop() + assert len(stopwatch._laps) == len(stopwatch.lap_times) + del(stopwatch) + + def test_stop_accuracy(self): + "Checks accuracy of calculate_time() method." + stopwatch = Stopwatch() + stopwatch.start() + sleep(0.1) + stopwatch.stop() + assert 0.100 <= stopwatch.total_time <= 0.110 # within 10ms tolerance + del(stopwatch) From 5933f82aa671cb8f055e606e75576139c799499b Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 20:52:52 -0800 Subject: [PATCH 09/15] build: Added PyTest cache to unbuild script. --- unbuild | 1 + 1 file changed, 1 insertion(+) diff --git a/unbuild b/unbuild index d5e10f8..59c1f8f 100755 --- a/unbuild +++ b/unbuild @@ -12,6 +12,7 @@ read -p "Do you want to continue? [Y/n]: " if [[ $REPLY =~ ^[Yy]$ ]] then + rm -rf .pytest_cache # remove pytest cache directory if exists rm -rf build # remove build directory if exists rm -rf dist # remove distribution directory if exists rm -rf jutl.egg-info # remove egg info directory if exists From 9c7c6cf4b6be3cfacfcfed002f7943832f3c83c4 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 20:55:53 -0800 Subject: [PATCH 10/15] fix: Removeed unnecessary space. --- jutl/timers/stopwatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jutl/timers/stopwatch.py b/jutl/timers/stopwatch.py index 7bd4246..db356b8 100644 --- a/jutl/timers/stopwatch.py +++ b/jutl/timers/stopwatch.py @@ -12,7 +12,7 @@ class Stopwatch(): """ def __init__(self, name: str = None): "Initialization method." - self.name : str = name + self.name: str = name self._start_time: float self._stop_time: float self.total_time: float = None From 55295e3248a380a6afd784c94b4c9a1177497845 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 22:59:35 -0800 Subject: [PATCH 11/15] test: Polished tests for Stopwatch. --- jutl/timers/stopwatch.py | 24 ++++++++++++------------ test/timers/test_stopwatch.py | 16 ++++++++++------ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/jutl/timers/stopwatch.py b/jutl/timers/stopwatch.py index db356b8..80ee43b 100644 --- a/jutl/timers/stopwatch.py +++ b/jutl/timers/stopwatch.py @@ -62,67 +62,67 @@ def __iter__(self) -> iter: """ return iter(self.lap_times) - def __eq__(self, other): + def __eq__(self, other) -> bool: """ Tells the interpreter how this class handles equal operators. """ return self.total_time == other.total_time - def __ne__(self, other): + def __ne__(self, other) -> bool: """ Tells the interpreter how this class handles not equal operators. """ return self.total_time != other.total_time - def __gt__(self, other): + def __gt__(self, other) -> bool: """ Tells the interpreter how this class handles greater than operators. """ return self.total_time > other.total_time - def __ge__(self, other): + def __ge__(self, other) -> bool: """ Tells the interpreter how this class handles greater or equal operators. """ return self.total_time >= other.total_time - def __lt__(self, other): + def __lt__(self, other) -> bool: """ Tells the interpreter how this class handles less than operators. """ return self.total_time < other.total_time - def __le__(self, other): + def __le__(self, other) -> bool: """ Tells the interpreter how this class handles less than or equal operators. """ return self.total_time <= other.total_time - def __add__(self, other): + def __add__(self, other) -> float: """ Tells the interpreter how to sum these objects. """ return self.total_time + other.total_time - def __sub__(self, other): + def __sub__(self, other) -> float: """ Tells the interpreter how to subtract these objects. """ return self.total_time - other.total_time - def __mul__(self, multiplier): + def __mul__(self, multiplier) -> float: """ Tells the interpreter how to subtract these objects. """ return self.total_time * multiplier - def __truediv__(self, other): + def __truediv__(self, other) -> float: """ Tells the interpreter how to subtract these objects. """ @@ -163,11 +163,11 @@ def stop(self): self.lap(self._stop_time) - def _calculate_time(self, t1: float, t2: float) -> float: + def _calculate_time(self, time1: float, time2: float) -> float: """ Returns the difference in time where t2>t1. """ - return t2 - t1 + return time2 - time1 def reset(self): diff --git a/test/timers/test_stopwatch.py b/test/timers/test_stopwatch.py index ce0140a..0f6f7bf 100644 --- a/test/timers/test_stopwatch.py +++ b/test/timers/test_stopwatch.py @@ -211,12 +211,12 @@ def test_sub(self): def test_mul(self): "Tests the multiply operator." - stopwatch1 = Stopwatch() - stopwatch1.start() + stopwatch = Stopwatch() + stopwatch.start() sleep(0.01) - stopwatch1.stop() - assert stopwatch1 * 2 == stopwatch1.total_time * 2 - del(stopwatch1) + stopwatch.stop() + assert stopwatch * 2 == stopwatch.total_time * 2 + del(stopwatch) def test_truediv(self): "Tests the divide operator." @@ -232,6 +232,7 @@ def test_truediv(self): del(stopwatch1) del(stopwatch2) + class TestRobustness(): def test_equal_list_sizes(self): """ @@ -258,7 +259,10 @@ def test_equal_list_sizes(self): del(stopwatch) def test_stop_accuracy(self): - "Checks accuracy of calculate_time() method." + """ + Checks accuracy of + calculate_time() method. + """ stopwatch = Stopwatch() stopwatch.start() sleep(0.1) From 908ec543f8129f49f3278c6278ae3673d9ecaf8f Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 23:00:10 -0800 Subject: [PATCH 12/15] test: Implemented test cases for Timer. --- jutl/timers/timer.py | 181 +++++++++++++++++++++++++++- test/timers/test_timer.py | 248 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 test/timers/test_timer.py diff --git a/jutl/timers/timer.py b/jutl/timers/timer.py index e253fa1..e8d3fa1 100644 --- a/jutl/timers/timer.py +++ b/jutl/timers/timer.py @@ -1,2 +1,181 @@ +# Module imports +from time import time, sleep + # External class visibility -__all__ = [] \ No newline at end of file +__all__ = ['Timer'] + + +class Timer(): + """ + Class which acts as a timer + with time methods. + """ + def __init__(self, name: str = None): + "Inisialization method." + self.name: str = name + self._start_time: float + self._pause_time: float = None + self._paused_time: float = None + self._stop_time: float + self.total_time: float = None + + def __repr__(self) -> str: + """ + Tells the interpreter how + to represent this class. + """ + if self.name is None: + if self.total_time is None: + return "Timer()" + else: + return f"Timer({round(self.total_time, 2)}s)" + else: + if self.total_time is None: + return f"Timer({self.name})" + else: + return f"Timer({self.name}, {round(self.total_time, 2)}s)" + + def __call__(self): + """ + Tells the interpreter what to + do when an object of this + class is called directly. + """ + if self.total_time: + print(f"{self.name} total time: {round(self.total_time, 2)}s") + else: + print("There is no recorded time.") + + def __len__(self): + """ + Tells the interpreter what to + consider this class' length. + """ + if self.total_time // 60 == 1: + return int(self.total_time // 60) + elif self.total_time // 3600 == 1: + return int(self.total_time // 3600) + else: + return int(self.total_time) + + def __eq__(self, other) -> bool: + """ + Tells the interpreter how this class + handles equal operators. + """ + return self.total_time == other.total_time + + def __ne__(self, other) -> bool: + """ + Tells the interpreter how this class + handles not equal operators. + """ + return self.total_time != other.total_time + + def __gt__(self, other) -> bool: + """ + Tells the interpreter how this class + handles greater than operators. + """ + return self.total_time > other.total_time + + def __ge__(self, other) -> bool: + """ + Tells the interpreter how this class + handles greater or equal operators. + """ + return self.total_time >= other.total_time + + def __lt__(self, other) -> bool: + """ + Tells the interpreter how this class + handles less than operators. + """ + return self.total_time < other.total_time + + def __le__(self, other) -> bool: + """ + Tells the interpreter how this class + handles less than or equal operators. + """ + return self.total_time <= other.total_time + + def __add__(self, other) -> float: + """ + Tells the interpreter how to sum these objects. + """ + return self.total_time + other.total_time + + def __sub__(self, other) -> float: + """ + Tells the interpreter how to subtract these objects. + """ + return self.total_time - other.total_time + + def __mul__(self, multiplier) -> float: + """ + Tells the interpreter how to subtract these objects. + """ + return self.total_time * multiplier + + def __truediv__(self, other) -> float: + """ + Tells the interpreter how to subtract these objects. + """ + return self.total_time / other.total_time + + + def start(self): + """ + Starts the timer by + initializing an object attribute. + """ + self._start_time = time() + + + def pause(self, duration: float = None): + """ + Pauses the timer. + """ + self._pause_time = time() + if duration: + sleep(duration) + + + def resume(self): + """ + Resumes the timer. + """ + if self._paused_time: + self._paused_time += self._calculate_time(self._pause_time, time()) + else: + self._paused_time = self._calculate_time(self._pause_time, time()) + + + def stop(self): + """ + Stops the timer and calculates + the total time passed. + """ + if self._pause_time and not self._paused_time: # if stopped while paused + self.resume() + self._stop_time = time() + self.total_time = self._calculate_time(self._start_time, self._stop_time) + + + def _calculate_time(self, time1: float, time2: float) -> float: + """ + Returns the difference in time where t2>t1. + """ + return time2 - time1 + + + def reset(self): + """ + Resets all timer attributes. + """ + self._start_time = None + self._pause_time = None + self._paused_time = None + self._stop_time = None + self.total_time = None diff --git a/test/timers/test_timer.py b/test/timers/test_timer.py new file mode 100644 index 0000000..70996bf --- /dev/null +++ b/test/timers/test_timer.py @@ -0,0 +1,248 @@ +# jutils/test/timers/test_timer.py +from jutl.timers import Timer +from time import sleep + +test_name: str = "Test name" + +class TestInit(): + def test_def(self): + "Tests if an object can be created from the Timer class." + timer = Timer() + assert timer is not None + del(timer) + + def test_name(self): + "Tests if naming a timer works." + timer = Timer() + assert timer.name == None + del(timer) + + timer = Timer(test_name) + assert timer.name == test_name + del(timer) + + +class TestDunder(): + def test_repr(self): + "Tests what is output for representation." + timer = Timer() + assert repr(timer) == f"Timer()" + del(timer) + + timer = Timer() + timer.start() + timer.stop() + assert repr(timer) == f"Timer({round(timer.total_time, 2)}s)" + del(timer) + + timer = Timer(test_name) + assert repr(timer) == f"Timer({test_name})" + del(timer) + + timer = Timer(test_name) + timer.start() + timer.stop() + assert repr(timer) == f"Timer({test_name}, {round(timer.total_time, 2)}s)" + del(timer) + + def test_len(self): + "Tests what is output for object length." + timer = Timer() + timer.start() + timer.stop() + assert len(timer) == 0 + del(timer) + + def test_equal(self): + "Tests the overridden equal function." + timer1 = Timer() + timer1.start() + timer1.stop() + timer2 = timer1 + assert timer1 == timer2 + del(timer1) + del(timer2) + + def test_not_equal(self): + "Tests the overridden not equal function." + timer1 = Timer() + timer2 = Timer() + timer1.start() + sleep(0.01) + timer1.stop() + timer2.start() + timer2.stop() + assert timer1 != timer2 + del(timer1) + del(timer2) + + def test_greater_than(self): + "Tests the overridden greater than function." + timer1 = Timer() + timer2 = Timer() + timer1.start() + sleep(0.01) + timer1.stop() + timer2.start() + timer2.stop() + assert timer1 > timer2 + del(timer1) + del(timer2) + + def test_greater_equal(self): + "Tests the overridden greater or equal function." + timer1 = Timer() + timer2 = Timer() + timer1.start() + sleep(0.01) + timer1.stop() + timer2.start() + timer2.stop() + assert timer1 > timer2 + del(timer1) + del(timer2) + + timer1 = Timer() + timer1.start() + timer1.stop() + timer2 = timer1 + assert timer1 == timer2 + del(timer1) + del(timer2) + + def test_less_than(self): + "Tests the overridden less than function." + timer1 = Timer() + timer2 = Timer() + timer1.start() + sleep(0.01) + timer1.stop() + timer2.start() + timer2.stop() + assert timer2 < timer1 + del(timer1) + del(timer2) + + def test_less_equal(self): + "Tests the overridden less or equal function." + timer1 = Timer() + timer2 = Timer() + timer1.start() + sleep(0.01) + timer1.stop() + timer2.start() + timer2.stop() + assert timer2 < timer1 + del(timer1) + del(timer2) + + timer1 = Timer() + timer1.start() + timer1.stop() + timer2 = timer1 + assert timer1 == timer2 + del(timer1) + del(timer2) + + def test_add(self): + "Tests the sum operator." + timer1 = Timer() + timer2 = Timer() + timer1.start() + sleep(0.01) + timer1.stop() + timer2.start() + timer2.stop() + assert timer1 + timer2 == timer1.total_time + timer2.total_time + del(timer1) + del(timer2) + + def test_sub(self): + "Tests the subtract operator." + timer1 = Timer() + timer2 = Timer() + timer1.start() + sleep(0.01) + timer1.stop() + timer2.start() + timer2.stop() + assert timer1 - timer2 == timer1.total_time - timer2.total_time + del(timer1) + del(timer2) + + def test_mul(self): + "Tests the multiply operator." + timer1 = Timer() + timer1.start() + sleep(0.01) + timer1.stop() + assert timer1 * 2 == timer1.total_time * 2 + del(timer1) + + def test_truediv(self): + "Tests the divide operator." + timer1 = Timer() + timer2 = Timer() + timer1.start() + sleep(0.02) + timer1.stop() + timer2.start() + sleep(0.01) + timer2.stop() + assert timer1 / timer2 == timer1.total_time / timer2.total_time + del(timer1) + del(timer2) + + +class TestRobustness(): + def test_pause_resume(self): + """ + Checks the pause and + resume methods work. + """ + timer = Timer() + + def test_multiple_pause(self): + """ + Checks correct time is calculated + with mulitple pauses & resumes. + """ + pass + + def test_pause_wait(self): + """ + Checks passed wait time + correctly executes. + """ + timer = Timer() + timer.start() + timer.pause(0.1) + timer.resume() + timer.stop() + assert timer._paused_time >= 0.1 + del(timer) + + def test_early_stop(self): + """ + Checks the timer correctly + stops if stopped while paused. + """ + timer = Timer() + timer.start() + timer.pause() + timer.stop() + assert timer._pause_time + assert timer._paused_time + del(timer) + + def test_stop_accuracy(self): + """ + Checks accuracy of + calculate_time() method. + """ + timer = Timer() + timer.start() + sleep(0.1) + timer.stop() + assert 0.100 <= timer.total_time <= 0.110 # within 10ms tolerance + del(timer) From 49f4bd85e9a5fdfb7659b230587a616c73fb9572 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Thu, 2 Feb 2023 23:02:46 -0800 Subject: [PATCH 13/15] test: Finished last of Timer tests. --- test/timers/test_timer.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/timers/test_timer.py b/test/timers/test_timer.py index 70996bf..cd69736 100644 --- a/test/timers/test_timer.py +++ b/test/timers/test_timer.py @@ -201,13 +201,29 @@ def test_pause_resume(self): resume methods work. """ timer = Timer() + timer.start() + timer.pause() + timer.resume() + timer.stop() + assert timer.total_time > 0 + del(timer) def test_multiple_pause(self): """ Checks correct time is calculated with mulitple pauses & resumes. """ - pass + timer = Timer() + timer.start() + timer.pause() + timer.resume() + timer.pause() + timer.resume() + timer.pause() + timer.resume() + timer.stop() + assert timer.total_time > 0 + del(timer) def test_pause_wait(self): """ From 93cd2853ee7fd683fbaf557b21b6ea0128bef4d7 Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Fri, 3 Feb 2023 16:36:53 -0800 Subject: [PATCH 14/15] build: Edited for 0.3.0 release. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index ce32cae..30c77b4 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ # Arguments git_name = "jutils" pypi_name = "jutl" -version = "0.2.0" +version = "0.3.0" python_version = ">=3.10" # Long description from README.md @@ -36,7 +36,7 @@ long_description_content_type='text/markdown', author='Jordan Welsman', author_email='jordan.welsman@outlook.com', - url='https://pypi.org/project/'+pypi_name+"/", + url='https://pypi.org/project/'+pypi_name+'/', download_url='https://github.com/JordanWelsman/jutils/tags', package_data={f'{pypi_name}': jutils_package_data}, python_requires=python_version, From 8684be03d730b2aea9d570ec3ca603257f20fbcd Mon Sep 17 00:00:00 2001 From: Jordan Welsman Date: Fri, 3 Feb 2023 16:37:56 -0800 Subject: [PATCH 15/15] build: Elevated development status to Beta. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 30c77b4..56212be 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ python_requires=python_version, # jutils package information classifiers=[ - 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: Education', 'License :: OSI Approved :: MIT License',