noiftimer.noiftimer

  1import warnings
  2from datetime import datetime
  3from typing import Any, Callable, Self
  4
  5
  6def time_it(loops: int = 1) -> Callable[..., Any]:
  7    """Decorator to time function execution time
  8    and print the results.
  9
 10    :param loops: How many times to loop the function,
 11    starting and stopping the timer before and after
 12    each loop."""
 13
 14    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
 15        def wrapper(*args, **kwargs) -> Any:
 16            timer = Timer(loops)
 17            for _ in range(loops):
 18                timer.start()
 19                result = func(*args, **kwargs)
 20                timer.stop()
 21            execution_time = timer.format_time(timer.average_elapsed_time, True)
 22            print(f"{func.__name__} average execution time: {execution_time}")
 23            return result
 24
 25        return wrapper
 26
 27    return decorator
 28
 29
 30class Timer:
 31    """Simple timer class that tracks total elapsed time
 32    and average time between calls to 'start' and 'stop'."""
 33
 34    def __init__(
 35        self, averaging_window_length: int = 10, subsecond_resolution: bool = True
 36    ):
 37        """:param averaging_window_length: Number of start/stop cycles
 38        to calculate the average elapsed time with.
 39
 40        :param subsecond_resolution: Whether to print formatted time
 41        strings with subsecond resolution or not."""
 42        self.start_time: datetime = datetime.now()
 43        self.stop_time: datetime = datetime.now()
 44        self.average_elapsed_time: float = 0
 45        self.history: list[float] = []
 46        self.elapsed_time: float = 0
 47        self.averaging_window_length: int = averaging_window_length
 48        self.started: bool = False
 49        self.subsecond_resolution = subsecond_resolution
 50
 51    @property
 52    def elapsed(self) -> float:
 53        """Return the currently elapsed time."""
 54        if self.started:
 55            return (datetime.now() - self.start_time).total_seconds()
 56        else:
 57            return (self.stop_time - self.start_time).total_seconds()
 58
 59    @property
 60    def elapsed_str(self) -> str:
 61        """Return the currently elapsed time
 62        as a formatted string."""
 63        return self.format_time(self.elapsed, self.subsecond_resolution)
 64
 65    def start(self) -> Self:
 66        """Start timer.
 67        Returns this Timer instance so
 68        timer start can be chained to
 69        Timer creation.
 70
 71        >>> timer = Timer().start()"""
 72        self.start_time = datetime.now()
 73        self.started = True
 74        return self
 75
 76    def stop(self):
 77        """Stop timer.
 78
 79        Calculates elapsed time and average elapsed time."""
 80        self.stop_time = datetime.now()
 81        self.started = False
 82        self.elapsed_time = (self.stop_time - self.start_time).total_seconds()
 83        self._save_elapsed_time()
 84        self.average_elapsed_time = sum(self.history) / (len(self.history))
 85
 86    def _save_elapsed_time(self):
 87        """Saves current elapsed time to the history buffer
 88        in a FIFO manner."""
 89        if len(self.history) >= self.averaging_window_length:
 90            self.history.pop(0)
 91        self.history.append(self.elapsed_time)
 92
 93    def current_elapsed_time(
 94        self, format: bool = True, subsecond_resolution: bool = False
 95    ) -> float | str:
 96        """Returns current elapsed without stopping the timer.
 97
 98        :param format: If True, elapsed time is returned as a string.
 99        If False, elapsed time is returned as a float."""
100        warnings.warn(
101            "current_elapsed_time is depreciated. Use 'elapsed' and 'elapsed_str' properties instead.",
102            FutureWarning,
103            2,
104        )
105        self.elapsed_time = (datetime.now() - self.start_time).total_seconds()
106        return (
107            self.format_time(self.elapsed_time, subsecond_resolution)
108            if format
109            else self.elapsed_time
110        )
111
112    def _get_time_unit(
113        self, num_seconds: float, seconds_per_unit: float, unit_suffix: str
114    ) -> tuple[float, str]:
115        """Determines the number of units in a given number of seconds
116        by integer division.
117
118        Returns a tuple containing the remaining number of seconds after division
119        as well as the number of units as a string with 'unit_suffix' appended to the string.
120
121        e.g. _get_time_unit(124, 60, 'm') will return (4, '2m')"""
122        num_units = int(num_seconds / seconds_per_unit)
123        if num_units > 0:
124            remainder = num_seconds - (num_units * seconds_per_unit)
125            return (remainder, f"{num_units}{unit_suffix}")
126        else:
127            return (num_seconds, "")
128
129    def format_time(
130        self, num_seconds: float, subsecond_resolution: bool = False
131    ) -> str:
132        """Returns num_seconds as a string with units.
133
134        :param subsecond_resolution: Include milliseconds
135        and microseconds with the output."""
136        microsecond = 0.000001
137        millisecond = 0.001
138        second = 1
139        seconds_per_minute = 60
140        seconds_per_hour = 3600
141        seconds_per_day = 86400
142        seconds_per_week = 604800
143        seconds_per_month = 2419200
144        seconds_per_year = 29030400
145        time_units = [
146            (seconds_per_year, "y"),
147            (seconds_per_month, "mn"),
148            (seconds_per_week, "w"),
149            (seconds_per_day, "d"),
150            (seconds_per_hour, "h"),
151            (seconds_per_minute, "m"),
152            (second, "s"),
153            (millisecond, "ms"),
154            (microsecond, "us"),
155        ]
156        if not subsecond_resolution:
157            time_units = time_units[:-2]
158        time_string = ""
159        for time_unit in time_units:
160            num_seconds, unit_string = self._get_time_unit(
161                num_seconds, time_unit[0], time_unit[1]
162            )
163            if unit_string != "":
164                time_string += f"{unit_string} "
165        return time_string.strip()
166
167    def get_stats(self, format: bool = True, subsecond_resolution: bool = False) -> str:
168        """Returns string for elapsed time and average elapsed time.
169
170        :param format: Times are returned as strings if True,
171        otherwise they're raw floats.
172
173        :param subsecond_resolution: Include milliseconds
174        and microseconds with the output."""
175        if format:
176            return f"elapsed time: {self.format_time(self.elapsed_time, subsecond_resolution)}\naverage elapsed time: {self.format_time(self.average_elapsed_time, subsecond_resolution)}"
177        else:
178            return f"elapsed time: {self.elapsed_time}s\naverage elapsed time: {self.average_elapsed_time}s"
def time_it(loops: int = 1) -> Callable[..., Any]:
 7def time_it(loops: int = 1) -> Callable[..., Any]:
 8    """Decorator to time function execution time
 9    and print the results.
10
11    :param loops: How many times to loop the function,
12    starting and stopping the timer before and after
13    each loop."""
14
15    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
16        def wrapper(*args, **kwargs) -> Any:
17            timer = Timer(loops)
18            for _ in range(loops):
19                timer.start()
20                result = func(*args, **kwargs)
21                timer.stop()
22            execution_time = timer.format_time(timer.average_elapsed_time, True)
23            print(f"{func.__name__} average execution time: {execution_time}")
24            return result
25
26        return wrapper
27
28    return decorator

Decorator to time function execution time and print the results.

Parameters
  • loops: How many times to loop the function, starting and stopping the timer before and after each loop.
class Timer:
 31class Timer:
 32    """Simple timer class that tracks total elapsed time
 33    and average time between calls to 'start' and 'stop'."""
 34
 35    def __init__(
 36        self, averaging_window_length: int = 10, subsecond_resolution: bool = True
 37    ):
 38        """:param averaging_window_length: Number of start/stop cycles
 39        to calculate the average elapsed time with.
 40
 41        :param subsecond_resolution: Whether to print formatted time
 42        strings with subsecond resolution or not."""
 43        self.start_time: datetime = datetime.now()
 44        self.stop_time: datetime = datetime.now()
 45        self.average_elapsed_time: float = 0
 46        self.history: list[float] = []
 47        self.elapsed_time: float = 0
 48        self.averaging_window_length: int = averaging_window_length
 49        self.started: bool = False
 50        self.subsecond_resolution = subsecond_resolution
 51
 52    @property
 53    def elapsed(self) -> float:
 54        """Return the currently elapsed time."""
 55        if self.started:
 56            return (datetime.now() - self.start_time).total_seconds()
 57        else:
 58            return (self.stop_time - self.start_time).total_seconds()
 59
 60    @property
 61    def elapsed_str(self) -> str:
 62        """Return the currently elapsed time
 63        as a formatted string."""
 64        return self.format_time(self.elapsed, self.subsecond_resolution)
 65
 66    def start(self) -> Self:
 67        """Start timer.
 68        Returns this Timer instance so
 69        timer start can be chained to
 70        Timer creation.
 71
 72        >>> timer = Timer().start()"""
 73        self.start_time = datetime.now()
 74        self.started = True
 75        return self
 76
 77    def stop(self):
 78        """Stop timer.
 79
 80        Calculates elapsed time and average elapsed time."""
 81        self.stop_time = datetime.now()
 82        self.started = False
 83        self.elapsed_time = (self.stop_time - self.start_time).total_seconds()
 84        self._save_elapsed_time()
 85        self.average_elapsed_time = sum(self.history) / (len(self.history))
 86
 87    def _save_elapsed_time(self):
 88        """Saves current elapsed time to the history buffer
 89        in a FIFO manner."""
 90        if len(self.history) >= self.averaging_window_length:
 91            self.history.pop(0)
 92        self.history.append(self.elapsed_time)
 93
 94    def current_elapsed_time(
 95        self, format: bool = True, subsecond_resolution: bool = False
 96    ) -> float | str:
 97        """Returns current elapsed without stopping the timer.
 98
 99        :param format: If True, elapsed time is returned as a string.
100        If False, elapsed time is returned as a float."""
101        warnings.warn(
102            "current_elapsed_time is depreciated. Use 'elapsed' and 'elapsed_str' properties instead.",
103            FutureWarning,
104            2,
105        )
106        self.elapsed_time = (datetime.now() - self.start_time).total_seconds()
107        return (
108            self.format_time(self.elapsed_time, subsecond_resolution)
109            if format
110            else self.elapsed_time
111        )
112
113    def _get_time_unit(
114        self, num_seconds: float, seconds_per_unit: float, unit_suffix: str
115    ) -> tuple[float, str]:
116        """Determines the number of units in a given number of seconds
117        by integer division.
118
119        Returns a tuple containing the remaining number of seconds after division
120        as well as the number of units as a string with 'unit_suffix' appended to the string.
121
122        e.g. _get_time_unit(124, 60, 'm') will return (4, '2m')"""
123        num_units = int(num_seconds / seconds_per_unit)
124        if num_units > 0:
125            remainder = num_seconds - (num_units * seconds_per_unit)
126            return (remainder, f"{num_units}{unit_suffix}")
127        else:
128            return (num_seconds, "")
129
130    def format_time(
131        self, num_seconds: float, subsecond_resolution: bool = False
132    ) -> str:
133        """Returns num_seconds as a string with units.
134
135        :param subsecond_resolution: Include milliseconds
136        and microseconds with the output."""
137        microsecond = 0.000001
138        millisecond = 0.001
139        second = 1
140        seconds_per_minute = 60
141        seconds_per_hour = 3600
142        seconds_per_day = 86400
143        seconds_per_week = 604800
144        seconds_per_month = 2419200
145        seconds_per_year = 29030400
146        time_units = [
147            (seconds_per_year, "y"),
148            (seconds_per_month, "mn"),
149            (seconds_per_week, "w"),
150            (seconds_per_day, "d"),
151            (seconds_per_hour, "h"),
152            (seconds_per_minute, "m"),
153            (second, "s"),
154            (millisecond, "ms"),
155            (microsecond, "us"),
156        ]
157        if not subsecond_resolution:
158            time_units = time_units[:-2]
159        time_string = ""
160        for time_unit in time_units:
161            num_seconds, unit_string = self._get_time_unit(
162                num_seconds, time_unit[0], time_unit[1]
163            )
164            if unit_string != "":
165                time_string += f"{unit_string} "
166        return time_string.strip()
167
168    def get_stats(self, format: bool = True, subsecond_resolution: bool = False) -> str:
169        """Returns string for elapsed time and average elapsed time.
170
171        :param format: Times are returned as strings if True,
172        otherwise they're raw floats.
173
174        :param subsecond_resolution: Include milliseconds
175        and microseconds with the output."""
176        if format:
177            return f"elapsed time: {self.format_time(self.elapsed_time, subsecond_resolution)}\naverage elapsed time: {self.format_time(self.average_elapsed_time, subsecond_resolution)}"
178        else:
179            return f"elapsed time: {self.elapsed_time}s\naverage elapsed time: {self.average_elapsed_time}s"

Simple timer class that tracks total elapsed time and average time between calls to 'start' and 'stop'.

Timer(averaging_window_length: int = 10, subsecond_resolution: bool = True)
35    def __init__(
36        self, averaging_window_length: int = 10, subsecond_resolution: bool = True
37    ):
38        """:param averaging_window_length: Number of start/stop cycles
39        to calculate the average elapsed time with.
40
41        :param subsecond_resolution: Whether to print formatted time
42        strings with subsecond resolution or not."""
43        self.start_time: datetime = datetime.now()
44        self.stop_time: datetime = datetime.now()
45        self.average_elapsed_time: float = 0
46        self.history: list[float] = []
47        self.elapsed_time: float = 0
48        self.averaging_window_length: int = averaging_window_length
49        self.started: bool = False
50        self.subsecond_resolution = subsecond_resolution
Parameters
  • averaging_window_length: Number of start/stop cycles to calculate the average elapsed time with.

  • subsecond_resolution: Whether to print formatted time strings with subsecond resolution or not.

elapsed: float

Return the currently elapsed time.

elapsed_str: str

Return the currently elapsed time as a formatted string.

def start(self) -> Self:
66    def start(self) -> Self:
67        """Start timer.
68        Returns this Timer instance so
69        timer start can be chained to
70        Timer creation.
71
72        >>> timer = Timer().start()"""
73        self.start_time = datetime.now()
74        self.started = True
75        return self

Start timer. Returns this Timer instance so timer start can be chained to Timer creation.

>>> timer = Timer().start()
def stop(self):
77    def stop(self):
78        """Stop timer.
79
80        Calculates elapsed time and average elapsed time."""
81        self.stop_time = datetime.now()
82        self.started = False
83        self.elapsed_time = (self.stop_time - self.start_time).total_seconds()
84        self._save_elapsed_time()
85        self.average_elapsed_time = sum(self.history) / (len(self.history))

Stop timer.

Calculates elapsed time and average elapsed time.

def current_elapsed_time( self, format: bool = True, subsecond_resolution: bool = False) -> float | str:
 94    def current_elapsed_time(
 95        self, format: bool = True, subsecond_resolution: bool = False
 96    ) -> float | str:
 97        """Returns current elapsed without stopping the timer.
 98
 99        :param format: If True, elapsed time is returned as a string.
100        If False, elapsed time is returned as a float."""
101        warnings.warn(
102            "current_elapsed_time is depreciated. Use 'elapsed' and 'elapsed_str' properties instead.",
103            FutureWarning,
104            2,
105        )
106        self.elapsed_time = (datetime.now() - self.start_time).total_seconds()
107        return (
108            self.format_time(self.elapsed_time, subsecond_resolution)
109            if format
110            else self.elapsed_time
111        )

Returns current elapsed without stopping the timer.

Parameters
  • format: If True, elapsed time is returned as a string. If False, elapsed time is returned as a float.
def format_time(self, num_seconds: float, subsecond_resolution: bool = False) -> str:
130    def format_time(
131        self, num_seconds: float, subsecond_resolution: bool = False
132    ) -> str:
133        """Returns num_seconds as a string with units.
134
135        :param subsecond_resolution: Include milliseconds
136        and microseconds with the output."""
137        microsecond = 0.000001
138        millisecond = 0.001
139        second = 1
140        seconds_per_minute = 60
141        seconds_per_hour = 3600
142        seconds_per_day = 86400
143        seconds_per_week = 604800
144        seconds_per_month = 2419200
145        seconds_per_year = 29030400
146        time_units = [
147            (seconds_per_year, "y"),
148            (seconds_per_month, "mn"),
149            (seconds_per_week, "w"),
150            (seconds_per_day, "d"),
151            (seconds_per_hour, "h"),
152            (seconds_per_minute, "m"),
153            (second, "s"),
154            (millisecond, "ms"),
155            (microsecond, "us"),
156        ]
157        if not subsecond_resolution:
158            time_units = time_units[:-2]
159        time_string = ""
160        for time_unit in time_units:
161            num_seconds, unit_string = self._get_time_unit(
162                num_seconds, time_unit[0], time_unit[1]
163            )
164            if unit_string != "":
165                time_string += f"{unit_string} "
166        return time_string.strip()

Returns num_seconds as a string with units.

Parameters
  • subsecond_resolution: Include milliseconds and microseconds with the output.
def get_stats(self, format: bool = True, subsecond_resolution: bool = False) -> str:
168    def get_stats(self, format: bool = True, subsecond_resolution: bool = False) -> str:
169        """Returns string for elapsed time and average elapsed time.
170
171        :param format: Times are returned as strings if True,
172        otherwise they're raw floats.
173
174        :param subsecond_resolution: Include milliseconds
175        and microseconds with the output."""
176        if format:
177            return f"elapsed time: {self.format_time(self.elapsed_time, subsecond_resolution)}\naverage elapsed time: {self.format_time(self.average_elapsed_time, subsecond_resolution)}"
178        else:
179            return f"elapsed time: {self.elapsed_time}s\naverage elapsed time: {self.average_elapsed_time}s"

Returns string for elapsed time and average elapsed time.

Parameters
  • format: Times are returned as strings if True, otherwise they're raw floats.

  • subsecond_resolution: Include milliseconds and microseconds with the output.