# Copyright 2025 European Union
# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
# SPDX-License-Identifier: EUPL-1.2
"""
Module implements a simple timer to measure the execution duration.
Basic usage:
.. code-block:: python
from mafw import timer
with Timer() as timer:
do_long_lasting_operation()
When exiting from the context manager, a message with the duration of the process is printed.
"""
import logging
from datetime import timedelta
from time import perf_counter
from types import TracebackType
[docs]
def rreplace(inp_string: str, old_string: str, new_string: str, counts: int) -> str:
"""
Utility function to replace a substring in a given string a certain number of times starting from the right-most one.
This function is mimicking the behavior of the string.replace method, but instead of replacing from the left, it
is replacing from the right.
:param inp_string: The input string
:type inp_string: str
:param old_string: The old substring to be matched. If empty, a ValueError is raised.
:type old_string: str
:param new_string: The new substring to be replaced
:type new_string: str
:param counts: The number of times the old substring has to be replaced.
:type counts: int
:return: The modified string
:rtype: str
:raises ValueError: if old_string is empty.
"""
if old_string == '':
raise ValueError('old_string cannot be empty')
li = inp_string.rsplit(old_string, counts)
return new_string.join(li)
[docs]
class Timer:
"""
The timer class.
"""
def __init__(self, suppress_message: bool = False) -> None:
"""
Constructor parameter:
:param suppress_message: A boolean flag to mute the timer
:type suppress_message: bool
"""
self._suppress_message = suppress_message
self.start = 0.0
self.end = 0.0
def __enter__(self) -> 'Timer':
"""
Context manager enter dunder method.
When an instance of the Timer class is created via the context manager, the start time attribute is set to the
current time, while the end is set to zero.
:return: The class instance.
"""
self.start = perf_counter()
self.end = 0.0
return self
def __exit__(
self, type_: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
) -> None:
"""
Context manager exit dunder method.
When the method is invoked, the end time is set to the current time, and, if the timer is not muted, a log
message with the total duration is printed.
:param type_: Exception type causing the context manager to exit. Defaults to None.
:type type_: type[BaseException], Optional
:param value: Exception that caused the context manager to exit. Defaults to None.
:type value: BaseException, Optional
:param traceback: Traceback. Defaults to None.
:type traceback: TracebackType
"""
self.end = perf_counter()
if not self._suppress_message:
logging.getLogger(__name__).info('Total execution time: %s' % self.format_duration())
@property
def duration(self) -> float:
"""
The elapsed time of the timer.
:return: Elapsed time in seconds.
"""
return self.end - self.start