Chronometer#

class stopuhr.Chronometer(printer: ~collections.abc.Callable[[str], None] = <built-in function print>, res: int = 2, log: bool = True)[source]#

Bases: object

Very high level benchmarking contextmanager and decorator.

Example

Measure the time taken in a code block.

>>> import stopuhr

>>> timer = stopuhr.Chronometer()
>>> with timer("Sleeping"):
>>>     time.sleep(0.2)

Instead of printing, one can pass any callable to the printer argument, e.g. a logger.

>>> import logging

>>> logger = logging.getLogger(__name__)
>>> logger.setLevel(logging.INFO)
>>> handler = logging.StreamHandler()
>>> handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
>>> logger.addHandler(handler)

>>> timer = stopuhr.Chronometer(printer=logger.info)
>>> with timer("Sleeping"):
>>>     time.sleep(0.1)
2025-03-27 19:11:10,912 - __main__ - INFO - Sleeping took 0.10s

The printer can be overridden for each call.

>>> timer = stopuhr.Chronometer(printer=logger.info)
>>> with timer("Sleeping", printer=logger.debug):
>>>     time.sleep(0.1)
2025-03-27 19:11:10,912 - __main__ - DEBUG - Sleeping took 0.10s

By default, the output is rounded to two decimal places. This can be changed with the res argument. The res argument can also be overridden for each call.

>>> timer = stopuhr.Chronometer(res=3)
>>> with timer("Sleeping"):
>>>     time.sleep(0.16189)
Sleeping took 0.162 s

>>> with timer("Sleeping", res=2):
>>>     time.sleep(0.16189)
Sleeping took 0.16 s

The chronometer is stateful and can be used to measure functions inside a loop. The log argument of each call can be used to suppress the output. It can also be set individually for each call.

>>> timer = stopuhr.Chronometer(log=False)

>>> for i in range(5):
>>>     with timer("Sleeping"):
>>>         time.sleep(0.2)

>>> # Print a summary with the mean and standard deviation of the durations.
>>> timer.summary()
Sleeping took 0.20 ± 0.00 s (n=5 -> total=1.00s)

The reset command resets the state of the timer, note that this function is happening in-place.

>>> timer.reset()

The previous behavior can be achieved with the start and stop methods. Here, the stop method also supports the log and res arguments.

>>> for i in range(5):
>>>     timer.start("Sleeping")
>>>     time.sleep(0.2)
>>>     timer.stop("Sleeping", log=False)

>>> timer.summary()
Sleeping took 0.20 ± 0.00 s (n=5 -> total=1.00s)

The stateful timer can also measure multiple durations at once.

>>> timer.reset()

>>> # Single duration
>>> with timer("A (single 0.2s sleep)", log=False):
>>>     time.sleep(0.2)

>>> for i in range(5):
>>>     with timer("B (multiple 0.2s sleeps)", log=False):
>>>         time.sleep(0.2)
>>>     with timer("C (multiple 0.1s sleeps)", log=False):
>>>         time.sleep(0.1)

>>> timer.summary()
A (single 0.2s sleep) took 0.20 s
B (multiple 0.2s sleeps) took 0.20 ± 0.00 s (n=5 -> total=1.00s)
C (multiple 0.1s sleeps) took 0.10 ± 0.00 s (n=5 -> total=0.50s)

The export method can be used to export the durations as a pandas DataFrame.

>>> import pandas as pd

>>> timer.reset()

>>> for i in range(5):
>>>     with timer("Sleeping", log=False):
>>>         time.sleep(0.2)
>>> for i in range(3):
>>>     with timer("Sleeping-2", log=False):
>>>         time.sleep(0.2)

>>> df = timer.export()
>>> print(df)
  Sleeping-2  Sleeping
0  0.2         0.2
1  0.2         0.2
2  0.2         0.2
3  0.2
4  0.2
static combine(*timers: Chronometer) Chronometer[source]#

Combine multiple Chronometers into one.

This is handy for multiprocessing situations.

Parameters:

*timers (Chronometer) – The Chronometers to combine. The new Chronometer will be created with the same printer, res and log settings as the first one.

Returns:

A new Chronometer with the combined durations.

Return type:

Chronometer

export() pd.DataFrame[source]#

Export the durations as a pandas DataFrame.

Returns:

A pandas DataFrame with the durations.

Return type:

pd.DataFrame

f(key: str, *, res: int | None = None, log: bool | None = None, printer: Callable[[str], None] | None = None, print_kwargs: list[str] | Literal['all'] | None = 'all')[source]#

Advanced decorator to also print the function arguments.

Instead of just passing the normal {key} took {duration:.{res}f}s message, this decorator will also add the function arguments to the message.

This behaviour is not possible to implement with the @contextmanager decorator, hence the need for this separate decorator.

Parameters:
  • key (str) – The key to store the duration under.

  • res (int | None, optional) – Override the objects default number of decimal places to round to if not None. Defaults to None.

  • log (bool | None, optional) – Override the objects default log setting if not None. Defaults to None.

  • printer (Callable[[str], None] | None, optional) – Override the objects function to print with if not None. Defaults to None.

  • print_kwargs (list[str] | bool, optional) – The arguments to be added to the key. If a list, only the arguments in the list will be added to the message. If True, all arguments will added. If False, no arguments will be added. Additions to the message will have the form: f”{key} (with {arg1=val1, arg2=val2, …})”. Defaults to False.

Raises:

ValueError – If any of the print_kwargs are not in the functions signature.

property log: bool#

Get the default log setting.

merge(other: Chronometer)[source]#

Merge the durations of another Chronometer into this one.

This is handy for multiprocessing situations.

Does not merge the idling_starts, only the durations.

Parameters:

other (Chronometer) – The Chronometer to merge with. This Chronometer will be modified in-place. The other Chronometer will not be modified.

parse_log(log: bool | None) bool[source]#

Parse the log argument.

If log is None, return the default log setting. If log is a bool, return it. Otherwise, raise an error.

Parameters:

log (bool | None) – Whether to log the duration at occurence.

Returns:

Whether to log the duration at occurence.

Return type:

bool

Raises:

ValueError – If log is not a boolean or None.

parse_printer(printer: Callable[[str], None] | None) Callable[[str], None][source]#

Parse the printer argument.

If the printer is None, return the default printer. If the printer is a callable, return it. Otherwise, raise an error.

Parameters:

printer (Printer | None) – The printer to use.

Returns:

The printer to use.

Return type:

Printer

Raises:

ValueError – If the printer is not a callable or None.

parse_res(res: int | None) int[source]#

Parse the res argument.

If res is None, return the default res. If res is an int, return it. Otherwise, raise an error.

Parameters:

res (int | None) – The number of decimal places to round to.

Returns:

The number of decimal places to round to.

Return type:

int

Raises:

ValueError – If res is not an int or None.

property printer: Callable[[str], None]#

Get the printer function.

property res: int#

Get the default number of decimal places to round to.

reset()[source]#

Reset the durations.

start(key: str)[source]#

Start the timer for a msg.

Parameters:

key (str) – The msg / key to store the start time under.

stop(key: str, *, res: int | None = None, log: bool | None = None, printer: Callable[[str], None] | None = None)[source]#

Stop the timer for a key.

Parameters:
  • key (str) – The key to store the duration under.

  • res (int | None, optional) – Override the objects default number of decimal places to round to if not None. Defaults to None.

  • log (bool | None, optional) – Override the objects default log setting if not None. Defaults to None.

  • printer (Callable[[str], None] | None, optional) – Override the objects function to print with if not None. Defaults to None.

summary(*, res: int | None = None, printer: Callable[[str], None] | None = None)[source]#

Print a summary of the durations.

Parameters:
  • res (int | None, optional) – Override the objects default number of decimal places to round to if not None. Defaults to None.

  • printer (Callable[[str], None] | None, optional) – Override the objects function to print with if not None. Defaults to None.