Source code for stouputils.ctx.measure_time


# Imports
from __future__ import annotations

import time
from collections.abc import Callable
from typing import Any

from ..print.message import debug
from .common import AbstractBothContextManager


# Context manager to measure execution time
[docs] class MeasureTime(AbstractBothContextManager["MeasureTime"]): """ Context manager to measure execution time. This context manager measures the execution time of the code block it wraps and prints the result using a specified print function. Args: print_func (Callable): Function to use to print the execution time (e.g. debug, info, warning, error, etc.). message (str): Message to display with the execution time. Defaults to "Execution time". perf_counter (bool): Whether to use time.perf_counter_ns or time.time_ns. Defaults to True. Examples: .. code-block:: python > import time > import stouputils as stp > with stp.MeasureTime(stp.info, message="My operation"): ... time.sleep(0.5) > # [INFO HH:MM:SS] My operation: 500.123ms (500123456ns) > with stp.MeasureTime(): # Uses debug by default ... time.sleep(0.1) > # [DEBUG HH:MM:SS] Execution time: 100.456ms (100456789ns) """ def __init__( self, print_func: Callable[..., None] = debug, message: str = "Execution time", perf_counter: bool = True ) -> None: self.print_func: Callable[..., None] = print_func """ Function to use for printing the execution time """ self.message: str = message """ Message to display with the execution time """ self.perf_counter: bool = perf_counter """ Whether to use time.perf_counter_ns or time.time_ns """ self.ns: Callable[[], int] = time.perf_counter_ns if perf_counter else time.time_ns """ Time function to use """ self.start_ns: int = 0 """ Start time in nanoseconds """ def __enter__(self) -> MeasureTime: """ Enter context manager, record start time """ self.start_ns = self.ns() return self def __exit__(self, exc_type: type[BaseException]|None, exc_val: BaseException|None, exc_tb: Any|None) -> None: """ Exit context manager, calculate duration and print """ # Measure the execution time (nanoseconds and seconds) total_ns: int = self.ns() - self.start_ns total_ms: float = total_ns / 1_000_000 total_s: float = total_ns / 1_000_000_000 # Print the execution time (nanoseconds if less than 0.1s, seconds otherwise) if total_ms < 100: self.print_func(f"{self.message}: {total_ms:.3f}ms ({total_ns}ns)") elif total_s < 60: self.print_func(f"{self.message}: {(total_s):.5f}s") else: minutes: int = int(total_s) // 60 seconds: int = int(total_s) % 60 if minutes < 60: self.print_func(f"{self.message}: {minutes}m {seconds}s") else: hours: int = minutes // 60 minutes: int = minutes % 60 if hours < 24: self.print_func(f"{self.message}: {hours}h {minutes}m {seconds}s") else: days: int = hours // 24 hours: int = hours % 24 self.print_func(f"{self.message}: {days}d {hours}h {minutes}m {seconds}s") async def __aenter__(self) -> MeasureTime: """ Enter async context manager, record start time """ return self.__enter__() async def __aexit__(self, exc_type: type[BaseException]|None, exc_val: BaseException|None, exc_tb: Any|None) -> None: """ Exit async context manager, calculate duration and print """ self.__exit__(exc_type, exc_val, exc_tb)