File size: 5,470 Bytes
33ba306
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""
ScriptTime class
records timepoints in a python script

new:
    functional
    added periter to print the average time per supplied number of iterations
    created formattime to handle times less than 1 ms
    removed secondstostr and replaced all calls with formattime
    added function profiling
    added toggle for profiling
    changed from time.time() to time.clock() which seems to give much higher resolution
    ---1.0---
    removed timepoint function (now redundant with profiling capability)
    updated print profile data function to be more detailed and easier to read
    ---1.1---
    ---1.2

to add:
    use time.time() in unix and time.clock() in windows
"""
import sys
import time
import numpy as np
import datetime


class ScriptTime(object):
    def __init__(self, profile=False):
        """
        Class for storing timepoints in a python script
        profile toggles whether the profiling functionality of the class will operate
        """
        self._start_seconds = time.time()  # start time (seconds since epoch)
        self._end_seconds = None  # end time (seconds since epoch)
        self._start_clock = time.localtime()  # start clock time
        self._end_clock = None  # end clock time
        self.profile = profile  # toggle for profiling functions
        self.profiles = {}

    def __str__(self):
        """The string that is returned when printed"""
        return f'{self.__class__.__name__} initiated at {self.start_time}'

    def __repr__(self):
        """The representation that is returned"""
        return f'{self.__class__.__name__}({self.start_time})'

    @property
    def start_time(self):
        return time.strftime("%I:%M:%S %p", self._start_clock)

    @property
    def end_time(self):
        if self._end_clock is None:
            return None
        return time.strftime("%I:%M:%S %p", self._end_clock)

    @property
    def elapsed_time(self):
        if self._end_seconds is None:
            return time.time() - self._start_seconds
        return self._end_seconds - self._start_seconds

    def clearprofiles(self):
        """clears the profile data"""
        self.profiles = {}

    def formattime(self, t):
        """
        Formats a time value in seconds to the appropriate string. This is now included for legacy support.
        roughly based on formattime 
        """
        return str(datetime.timedelta(t))

    def periter(self, num):
        """
        calculated elapsed time per unit iteration (designed for timing scripts)
        """
        sys.stdout.write('Average time per iteration: %s\n' % (self.formattime(self.elapsed_time / float(num))))

    def printelapsed(self):
        """prints the elapsed time of the object"""
        if self._end_seconds is None:
            self.triggerend()
        sys.stdout.write(f'Elapsed time: {datetime.timedelta(seconds=self.elapsed_time)}\n')

    def printend(self):
        """prints the end time and the elapsed time of the object"""
        if self._end_seconds is None:
            self.triggerend()
        sys.stdout.write(f'End time: {self.end_time} (elapsed: {datetime.timedelta(seconds=self.elapsed_time)})\n')

    def printprofiles(self):
        """prints the data for the profiled functions"""
        sys.stdout.write('\nFunction profile data:\n')
        sys.stdout.write(
            '%15s  %6s  %13s  %13s  %13s  %13s\n' % ('function', 'called', 'avg', 'standard_deviation', 'max', 'min'))
        for fname, data in self.profiles.items():
            avg = sum(data[1]) / len(data[1])
            sys.stdout.write('%15s  %6d  %13s  %13s  %13s  %13s\n' % (
                fname,
                data[0],
                self.formattime(avg),
                self.formattime(np.sqrt(sum((i - avg) ** 2 for i in data[1]) / (len(data[1]) - 1))),
                self.formattime(max(data[1])),
                self.formattime(min(data[1]))
            ))

    def printstart(self):
        """prints the start (trigger) time of the object"""
        sys.stdout.write('Start time: %s\n' % (time.strftime('%I:%M:%S %p', self._start_clock)))

    def profilefn(self, fn):
        """generates a profiled version of the supplied function"""

        # from functools import wraps # unsure why these lines are present
        # @wraps(fn)
        def with_profiling(*args, **kwargs):
            """decorates function with profiling commands"""
            start_time = time.perf_counter()  # time that the function was called
            ret = fn(*args, **kwargs)  # calls the function

            elapsed_time = time.perf_counter() - start_time  # end time of the function
            if fn.__name__ not in self.profiles:  # generates a dictionary key based on the function name if not present
                self.profiles[fn.__name__] = [0, []]  # [number of times called, [list of durations]]
            self.profiles[fn.__name__][0] += 1
            self.profiles[fn.__name__][1].append(elapsed_time)
            return ret  # returns the calculated call of the function

        if self.profile is True:
            return with_profiling  # returns the decorated function
        else:
            return fn

    def triggerend(self):
        """triggers endpoint and calculates elapsed time since start"""
        self._end_seconds = time.time()
        self._end_clock = time.localtime()


if __name__ == '__main__':
    st = ScriptTime()
    time.sleep(2.)
    st.triggerend()
    st.printend()