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"
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.
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'.
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.
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()
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.
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.
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.
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.