File size: 4,822 Bytes
068ed60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""
    Module execution_timer provides an ExecutionTimer class
        to measure the execution time of a code block.
    It offers two usage options: as a context manager and as a decorator.

    * Example usage as a context manager:
        with ExecutionTimer():
            main()

    * Example usage as a decorator:
        @execution_timer
        def main():
            # Code block to measure execution time
"""

from datetime import datetime
from time import perf_counter_ns

from dataclasses import dataclass
from rich.console import Console


@dataclass(slots=True)
class ExecutionTimer:
    """
        ExecutionTimer is a context manager that measures the execution time of a code block.
        It captures the start time, end time, and duration of the code block.
    """

    start_date: datetime = None
    end_date: datetime = None
    start_time_ns: int = None
    end_time_ns: int = None
    console: Console = Console()

    def __post_init__(self):
        self.start_date = datetime.now()
        self.start_time_ns = perf_counter_ns()

    def __enter__(self) -> 'ExecutionTimer':
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            self.end_date = datetime.now()
            self.end_time_ns = perf_counter_ns()
            self.display_time()
        except AttributeError:
            print('An error occurred: __exit__')

    @staticmethod
    def current_datetime(date: datetime) -> str:
        """
            Formats a datetime object as a string in the format 'YYYY-MM-DD HH:MM:SS'.
        """

        return f'[yellow]{date.year}-{date.month:02d}-{date.day:02d}' \
               f' [white bold]{date.hour:02d}:{date.minute:02d}:{date.second:02d}'

    def calculate_duration(self) -> str:
        """
            Calculates the duration of the code block in hours, minutes, seconds, milliseconds,
            microseconds, and nanoseconds.
        """

        duration_ns: int = self.end_time_ns - self.start_time_ns
        duration_s, duration_ns = map(int, divmod(duration_ns, 1_000_000_000))
        duration_ms, duration_ns = map(int, divmod(duration_ns, 1_000_000))
        duration_us, duration_ns = map(int, divmod(duration_ns, 1_000))

        hours, remainder = map(int, divmod(duration_s, 3600))
        minutes, seconds = map(int, divmod(remainder, 60))

        return f'[white bold]{hours:02d}:{minutes:02d}:{seconds:02d}:' \
               f'{duration_ms:03d}:{duration_us:03d}:{duration_ns:03d}'

    def calculate_duration_alt(self) -> tuple[float, ...]:
        """
            Calculates the duration of the code block in hours, minutes, and seconds
            using an alternative method.
        """

        duration_ns: int = self.end_time_ns - self.start_time_ns
        hours_alt: float = duration_ns / 1_000_000_000 / 60 / 60
        minutes_alt: float = duration_ns / 1_000_000_000 / 60
        seconds_alt: float = duration_ns / 1_000_000_000

        return hours_alt, minutes_alt, seconds_alt

    def display_time(self):
        """
            Displays the start date, end date, and duration of the code block execution.
        """

        start_date_str: str = self.current_datetime(self.start_date)
        end_date_str: str = self.current_datetime(self.end_date)
        duration: str = self.calculate_duration()
        hours_alt, minutes_alt, seconds_alt = map(
            float, self.calculate_duration_alt())

        self.console.print(
            '\n[bold white]β•šβ•β•β•β•β•β•β•β•β•β•β• EXECUTION TIME ═══════════╝')
        self.console.print(
            '[bold bright_yellow]        YYYY-MM-DD HH:MM:SS:ms :Β΅s :ns')
        self.console.print(
            f'[bright_red bold][[bold white]START[bright_red bold]] {start_date_str}')
        self.console.print(
            f'[bright_red bold][[bold white]END[bright_red bold]]   {end_date_str}')
        self.console.print(
            f'[bright_red bold][[bold white]TIME[bright_red bold]]  [bold bright_yellow]YYYY-MM-DD {duration}')
        self.console.print('[bright_red bold]                   ^^^^^^^^^^^^')
        self.console.print(
            f'[bright_red bold][[bold white]TIME[bright_red bold]]  [white bold]{hours_alt:.9f} hours')
        self.console.print(
            f'[bright_red bold][[bold white]TIME[bright_red bold]]  [white bold]{minutes_alt:.9f} minutes')
        self.console.print(
            f'[bright_red bold][[bold white]TIME[bright_red bold]]  [white bold]{seconds_alt:.9f} seconds')


def execution_timer(func):
    """
        Decorator that measures the execution time of a function using ExecutionTimer.
    """

    def wrapper(*args, **kwargs):
        with ExecutionTimer():
            result = func(*args, **kwargs)
        return result

    return wrapper