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 @staticmethod 113 def format_time(num_seconds: float, subsecond_resolution: bool = False) -> str: 114 """Returns num_seconds as a string with units. 115 116 :param subsecond_resolution: Include milliseconds 117 and microseconds with the output.""" 118 microsecond = 0.000001 119 millisecond = 0.001 120 second = 1 121 seconds_per_minute = 60 122 seconds_per_hour = 3600 123 seconds_per_day = 86400 124 seconds_per_week = 604800 125 seconds_per_month = 2419200 126 seconds_per_year = 29030400 127 time_units = [ 128 (seconds_per_year, "y"), 129 (seconds_per_month, "mn"), 130 (seconds_per_week, "w"), 131 (seconds_per_day, "d"), 132 (seconds_per_hour, "h"), 133 (seconds_per_minute, "m"), 134 (second, "s"), 135 (millisecond, "ms"), 136 (microsecond, "us"), 137 ] 138 if not subsecond_resolution: 139 time_units = time_units[:-2] 140 time_string = "" 141 for time_unit in time_units: 142 unit_amount, num_seconds = divmod(num_seconds, time_unit[0]) 143 if unit_amount > 0: 144 time_string += f"{int(unit_amount)}{time_unit[1]} " 145 if time_string == "": 146 return f"<1{time_units[-1][1]}" 147 return time_string.strip() 148 149 def get_stats(self, format: bool = True, subsecond_resolution: bool = False) -> str: 150 """Returns string for elapsed time and average elapsed time. 151 152 :param format: Times are returned as strings if True, 153 otherwise they're raw floats. 154 155 :param subsecond_resolution: Include milliseconds 156 and microseconds with the output.""" 157 if format: 158 return f"elapsed time: {self.format_time(self.elapsed_time, subsecond_resolution)}\naverage elapsed time: {self.format_time(self.average_elapsed_time, subsecond_resolution)}" 159 else: 160 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 @staticmethod 114 def format_time(num_seconds: float, subsecond_resolution: bool = False) -> str: 115 """Returns num_seconds as a string with units. 116 117 :param subsecond_resolution: Include milliseconds 118 and microseconds with the output.""" 119 microsecond = 0.000001 120 millisecond = 0.001 121 second = 1 122 seconds_per_minute = 60 123 seconds_per_hour = 3600 124 seconds_per_day = 86400 125 seconds_per_week = 604800 126 seconds_per_month = 2419200 127 seconds_per_year = 29030400 128 time_units = [ 129 (seconds_per_year, "y"), 130 (seconds_per_month, "mn"), 131 (seconds_per_week, "w"), 132 (seconds_per_day, "d"), 133 (seconds_per_hour, "h"), 134 (seconds_per_minute, "m"), 135 (second, "s"), 136 (millisecond, "ms"), 137 (microsecond, "us"), 138 ] 139 if not subsecond_resolution: 140 time_units = time_units[:-2] 141 time_string = "" 142 for time_unit in time_units: 143 unit_amount, num_seconds = divmod(num_seconds, time_unit[0]) 144 if unit_amount > 0: 145 time_string += f"{int(unit_amount)}{time_unit[1]} " 146 if time_string == "": 147 return f"<1{time_units[-1][1]}" 148 return time_string.strip() 149 150 def get_stats(self, format: bool = True, subsecond_resolution: bool = False) -> str: 151 """Returns string for elapsed time and average elapsed time. 152 153 :param format: Times are returned as strings if True, 154 otherwise they're raw floats. 155 156 :param subsecond_resolution: Include milliseconds 157 and microseconds with the output.""" 158 if format: 159 return f"elapsed time: {self.format_time(self.elapsed_time, subsecond_resolution)}\naverage elapsed time: {self.format_time(self.average_elapsed_time, subsecond_resolution)}" 160 else: 161 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.
113 @staticmethod 114 def format_time(num_seconds: float, subsecond_resolution: bool = False) -> str: 115 """Returns num_seconds as a string with units. 116 117 :param subsecond_resolution: Include milliseconds 118 and microseconds with the output.""" 119 microsecond = 0.000001 120 millisecond = 0.001 121 second = 1 122 seconds_per_minute = 60 123 seconds_per_hour = 3600 124 seconds_per_day = 86400 125 seconds_per_week = 604800 126 seconds_per_month = 2419200 127 seconds_per_year = 29030400 128 time_units = [ 129 (seconds_per_year, "y"), 130 (seconds_per_month, "mn"), 131 (seconds_per_week, "w"), 132 (seconds_per_day, "d"), 133 (seconds_per_hour, "h"), 134 (seconds_per_minute, "m"), 135 (second, "s"), 136 (millisecond, "ms"), 137 (microsecond, "us"), 138 ] 139 if not subsecond_resolution: 140 time_units = time_units[:-2] 141 time_string = "" 142 for time_unit in time_units: 143 unit_amount, num_seconds = divmod(num_seconds, time_unit[0]) 144 if unit_amount > 0: 145 time_string += f"{int(unit_amount)}{time_unit[1]} " 146 if time_string == "": 147 return f"<1{time_units[-1][1]}" 148 return time_string.strip()
Returns num_seconds as a string with units.
Parameters
- subsecond_resolution: Include milliseconds and microseconds with the output.
150 def get_stats(self, format: bool = True, subsecond_resolution: bool = False) -> str: 151 """Returns string for elapsed time and average elapsed time. 152 153 :param format: Times are returned as strings if True, 154 otherwise they're raw floats. 155 156 :param subsecond_resolution: Include milliseconds 157 and microseconds with the output.""" 158 if format: 159 return f"elapsed time: {self.format_time(self.elapsed_time, subsecond_resolution)}\naverage elapsed time: {self.format_time(self.average_elapsed_time, subsecond_resolution)}" 160 else: 161 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.