noiftimer

1from .noiftimer import Timer, time_it
2
3__version__ = "2.4.2"
4__all__ = ["Timer", "time_it"]
class Timer:
 69class Timer:
 70    """Simple timer class that tracks total elapsed time
 71    and average time between calls to `start()` and `stop()`."""
 72
 73    def __init__(
 74        self, averaging_window_length: int = 10, subsecond_resolution: bool = True
 75    ):
 76        """
 77        #### :params:
 78        * `averaging_window_length`: Number of start/stop cycles to calculate the average elapsed time with.
 79
 80        * `subsecond_resolution`: Whether to print formatted time strings with subsecond resolution or not.
 81        """
 82        self._start_time = time.time()
 83        self._stop_time = self.start_time
 84        self._elapsed = 0
 85        self._average_elapsed = 0
 86        self._history: deque[float] = deque([], averaging_window_length)
 87        self._started: bool = False
 88        self.subsecond_resolution = subsecond_resolution
 89        self._pauser = _Pauser()
 90
 91    @property
 92    def started(self) -> bool:
 93        """Returns whether the timer has been started and is currently running."""
 94        return self._started
 95
 96    @property
 97    def elapsed(self) -> float:
 98        """Returns the currently elapsed time."""
 99        if self._started:
100            return time.time() - self._start_time - self._pauser.pause_total
101        else:
102            return self._elapsed
103
104    @property
105    def elapsed_str(self) -> str:
106        """Returns the currently elapsed time as a formatted string."""
107        return self.format_time(self.elapsed, self.subsecond_resolution)
108
109    @property
110    def average_elapsed(self) -> float:
111        """Returns the average elapsed time."""
112        return self._average_elapsed
113
114    @property
115    def average_elapsed_str(self) -> str:
116        """Returns the average elapsed time as a formatted string."""
117        return self.format_time(self._average_elapsed, self.subsecond_resolution)
118
119    @property
120    def start_time(self) -> float:
121        """Returns the timestamp of the last call to `start()`."""
122        return self._start_time
123
124    @property
125    def stop_time(self) -> float:
126        """Returns the timestamp of the last call to `stop()`."""
127        return self._stop_time
128
129    @property
130    def history(self) -> deque[float]:
131        """Returns the history buffer for this timer."""
132        return self._history
133
134    @property
135    def is_paused(self) -> bool:
136        return self._pauser.paused
137
138    def start(self: Self) -> Self:
139        """Start the timer.
140
141        Returns this Timer instance so timer start can be chained to Timer creation if desired.
142
143        >>> timer = Timer().start()"""
144        if not self.started:
145            self._start_time = time.time()
146            self._started = True
147        return self
148
149    def stop(self):
150        """Stop the timer.
151
152        Calculates elapsed time and average elapsed time."""
153        if self.started:
154            self._stop_time = time.time()
155            self._started = False
156            self._elapsed = (
157                self._stop_time - self._start_time - self._pauser.pause_total
158            )
159            self._pauser.reset()
160            self._history.append(self._elapsed)
161            self._average_elapsed = sum(self._history) / (len(self._history))
162
163    def reset(self):
164        """Calls stop() then start() for convenience."""
165        self.stop()
166        self.start()
167
168    def pause(self):
169        """Pause the timer."""
170        self._pauser.pause()
171
172    def unpause(self):
173        """Unpause the timer."""
174        self._pauser.unpause()
175
176    @staticmethod
177    def format_time(num_seconds: float, subsecond_resolution: bool = False) -> str:
178        """Returns `num_seconds` as a string with units.
179
180        #### :params:
181
182        `subsecond_resolution`: Include milliseconds and microseconds with the output.
183        """
184        microsecond = 0.000001
185        millisecond = 0.001
186        second = 1
187        seconds_per_minute = 60
188        seconds_per_hour = 3600
189        seconds_per_day = 86400
190        seconds_per_week = 604800
191        seconds_per_month = 2419200
192        seconds_per_year = 29030400
193        time_units = [
194            (seconds_per_year, "y"),
195            (seconds_per_month, "mn"),
196            (seconds_per_week, "w"),
197            (seconds_per_day, "d"),
198            (seconds_per_hour, "h"),
199            (seconds_per_minute, "m"),
200            (second, "s"),
201            (millisecond, "ms"),
202            (microsecond, "us"),
203        ]
204        if not subsecond_resolution:
205            time_units = time_units[:-2]
206        time_string = ""
207        for time_unit in time_units:
208            unit_amount, num_seconds = divmod(num_seconds, time_unit[0])
209            if unit_amount > 0:
210                time_string += f"{int(unit_amount)}{time_unit[1]} "
211        if time_string == "":
212            return f"<1{time_units[-1][1]}"
213        return time_string.strip()
214
215    @property
216    def stats(self) -> str:
217        """Returns a string stating the currently elapsed time and the average elapsed time."""
218        return f"elapsed time: {self.elapsed_str}\naverage elapsed time: {self.average_elapsed_str}"

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)
73    def __init__(
74        self, averaging_window_length: int = 10, subsecond_resolution: bool = True
75    ):
76        """
77        #### :params:
78        * `averaging_window_length`: Number of start/stop cycles to calculate the average elapsed time with.
79
80        * `subsecond_resolution`: Whether to print formatted time strings with subsecond resolution or not.
81        """
82        self._start_time = time.time()
83        self._stop_time = self.start_time
84        self._elapsed = 0
85        self._average_elapsed = 0
86        self._history: deque[float] = deque([], averaging_window_length)
87        self._started: bool = False
88        self.subsecond_resolution = subsecond_resolution
89        self._pauser = _Pauser()

:params:

  • 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.

started: bool

Returns whether the timer has been started and is currently running.

elapsed: float

Returns the currently elapsed time.

elapsed_str: str

Returns the currently elapsed time as a formatted string.

average_elapsed: float

Returns the average elapsed time.

average_elapsed_str: str

Returns the average elapsed time as a formatted string.

start_time: float

Returns the timestamp of the last call to start().

stop_time: float

Returns the timestamp of the last call to stop().

history: collections.deque[float]

Returns the history buffer for this timer.

def start(self: Self) -> Self:
138    def start(self: Self) -> Self:
139        """Start the timer.
140
141        Returns this Timer instance so timer start can be chained to Timer creation if desired.
142
143        >>> timer = Timer().start()"""
144        if not self.started:
145            self._start_time = time.time()
146            self._started = True
147        return self

Start the timer.

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

>>> timer = Timer().start()
def stop(self):
149    def stop(self):
150        """Stop the timer.
151
152        Calculates elapsed time and average elapsed time."""
153        if self.started:
154            self._stop_time = time.time()
155            self._started = False
156            self._elapsed = (
157                self._stop_time - self._start_time - self._pauser.pause_total
158            )
159            self._pauser.reset()
160            self._history.append(self._elapsed)
161            self._average_elapsed = sum(self._history) / (len(self._history))

Stop the timer.

Calculates elapsed time and average elapsed time.

def reset(self):
163    def reset(self):
164        """Calls stop() then start() for convenience."""
165        self.stop()
166        self.start()

Calls stop() then start() for convenience.

def pause(self):
168    def pause(self):
169        """Pause the timer."""
170        self._pauser.pause()

Pause the timer.

def unpause(self):
172    def unpause(self):
173        """Unpause the timer."""
174        self._pauser.unpause()

Unpause the timer.

@staticmethod
def format_time(num_seconds: float, subsecond_resolution: bool = False) -> str:
176    @staticmethod
177    def format_time(num_seconds: float, subsecond_resolution: bool = False) -> str:
178        """Returns `num_seconds` as a string with units.
179
180        #### :params:
181
182        `subsecond_resolution`: Include milliseconds and microseconds with the output.
183        """
184        microsecond = 0.000001
185        millisecond = 0.001
186        second = 1
187        seconds_per_minute = 60
188        seconds_per_hour = 3600
189        seconds_per_day = 86400
190        seconds_per_week = 604800
191        seconds_per_month = 2419200
192        seconds_per_year = 29030400
193        time_units = [
194            (seconds_per_year, "y"),
195            (seconds_per_month, "mn"),
196            (seconds_per_week, "w"),
197            (seconds_per_day, "d"),
198            (seconds_per_hour, "h"),
199            (seconds_per_minute, "m"),
200            (second, "s"),
201            (millisecond, "ms"),
202            (microsecond, "us"),
203        ]
204        if not subsecond_resolution:
205            time_units = time_units[:-2]
206        time_string = ""
207        for time_unit in time_units:
208            unit_amount, num_seconds = divmod(num_seconds, time_unit[0])
209            if unit_amount > 0:
210                time_string += f"{int(unit_amount)}{time_unit[1]} "
211        if time_string == "":
212            return f"<1{time_units[-1][1]}"
213        return time_string.strip()

Returns num_seconds as a string with units.

:params:

subsecond_resolution: Include milliseconds and microseconds with the output.

stats: str

Returns a string stating the currently elapsed time and the average elapsed time.

def time_it(loops: int = 1) -> Callable[..., Any]:
10def time_it(loops: int = 1) -> Callable[..., Any]:
11    """Decorator to time function execution time and print the results.
12
13    #### :params:
14
15    `loops`: How many times to execute the decorated function,
16    starting and stopping the timer before and after each loop."""
17
18    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
19        @wraps(func)
20        def wrapper(*args: Any, **kwargs: Any) -> Any:
21            timer = Timer(loops)
22            result = None
23            for _ in range(loops):
24                timer.start()
25                result = func(*args, **kwargs)
26                timer.stop()
27            print(
28                f"{func.__name__} {'average ' if loops > 1 else ''}execution time: {timer.average_elapsed_str}"
29            )
30            return result
31
32        return wrapper
33
34    return decorator

Decorator to time function execution time and print the results.

:params:

loops: How many times to execute the decorated function, starting and stopping the timer before and after each loop.