custom-profiler 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+
2
+ from . import custum_profiler
3
+ from . import collecteur
4
+
5
+ from functools import partial
6
+
7
+ profiler = partial(custum_profiler.profiler, linePerline=False)
8
+ profiler_lbl = partial(custum_profiler.profiler, linePerline=True)
9
+ magic_profiler = custum_profiler.magic_profiler
10
+ profiler_collecteur = collecteur.profiler_collecteur
11
+ INTERACTIVITY_OPT_ENUM = collecteur.INTERACTIVITY_OPT_ENUM
@@ -0,0 +1,140 @@
1
+ import time
2
+ import sys
3
+ if sys.platform == 'linux':
4
+ import resource
5
+ import logging
6
+ from collections import OrderedDict
7
+
8
+ import psutil
9
+ from psutil._common import bytes2human
10
+ process = psutil.Process()
11
+
12
+ from custom_profiler.custum_logger import add_logging_level
13
+ from custom_profiler.human_readable_time import human_time_duration as htd
14
+
15
+
16
+ class INTERACTIVITY_OPT_ENUM :
17
+ ENABLE = "ENABLE" # thread (for memory peak follow) and interactive print
18
+ MF_NO_INTERAC = "MF_NO_INTERAC" # memory peak follow (with thread), no interacrtif print
19
+ DISABLE = "DISABLE" # no thread, no memory peak follow, no interacrtif print
20
+ AUTO = "AUTO" # if console is redirect in file (sys.stdout.isatty() == false) AUTO is equivalente to MF_NO_INTERAC else is equivalente to ENABLE
21
+
22
+ def get_ENUM_list(ENUM):
23
+ return [key for key in ENUM.__dict__ if key not in ["__main__", "__module__", "__doc__", '__dict__', '__weakref__']]
24
+
25
+
26
+ class profiler_collecteur(object):
27
+ _instance = None
28
+
29
+ def __new__(self):
30
+ if self._instance is None:
31
+ self._instance = super(profiler_collecteur, self).__new__(self)
32
+ self.profData = OrderedDict()
33
+ self.profThread = OrderedDict()
34
+ self.interractivity = INTERACTIVITY_OPT_ENUM.ENABLE
35
+ self.logger = None
36
+ self.start_time = time.perf_counter()
37
+ return self._instance
38
+
39
+ def options(self, interractivity = INTERACTIVITY_OPT_ENUM.ENABLE
40
+ , useLogger=False
41
+ , loggername = " ⚡"
42
+ , addCustumLvl= True
43
+ , profilerlvl = 25):
44
+
45
+ assert interractivity in get_ENUM_list(INTERACTIVITY_OPT_ENUM), f'interractivity {interractivity} must be in INTERACTIVITY_OPT_ENUM : {getEnumList(INTERACTIVITY_OPT_ENUM)}'
46
+
47
+ if interractivity == INTERACTIVITY_OPT_ENUM.AUTO :
48
+ if sys.stdout.isatty():
49
+ self.interractivity = INTERACTIVITY_OPT_ENUM.ENABLE
50
+ else :
51
+ self.interractivity = INTERACTIVITY_OPT_ENUM.MF_NO_INTERAC
52
+
53
+ if useLogger:
54
+ if self.interractivity == INTERACTIVITY_OPT_ENUM.ENABLE :
55
+ self.interractivity = INTERACTIVITY_OPT_ENUM.MF_NO_INTERAC
56
+ if addCustumLvl :
57
+ add_logging_level('PROFILER', profilerlvl)
58
+ logging.getLogger().setLevel("PROFILER")
59
+ self.logger = logging.getLogger(loggername).profiler
60
+ else :
61
+ logging.getLogger().setLevel("INFO")
62
+ self.logger = logging.getLogger(loggername).info
63
+ else :
64
+ self.logger = None
65
+
66
+ def save(self, fname, deltaTime, deltaMem, long_fname=None):
67
+ if fname in self.profData.keys():
68
+ self.profData[fname]["dt"] += deltaTime
69
+ self.profData[fname]["dm"] += deltaMem
70
+ self.profData[fname]["dm_list"].append(deltaMem)
71
+ self.profData[fname]["nbCall"] += 1
72
+ else :
73
+ self.profData[fname] = {"dt": deltaTime ,"dm": deltaMem, "dm_list": [deltaMem], "nbCall": 1}
74
+
75
+ t_str = htd(deltaTime)
76
+ value = f"{t_str}"
77
+ strmen = bytes2human(deltaMem)
78
+ if long_fname == None :
79
+ long_fname = fname
80
+ self.print_line(long_fname, value, strmen)
81
+
82
+ def thread_view(self, fname, deltaMem):
83
+ if fname in self.profThread.keys():
84
+ if deltaMem > self.profThread[fname]:
85
+ self.profThread[fname] = deltaMem
86
+ else :
87
+ self.profThread[fname] = deltaMem
88
+
89
+ def get_global_info(self):
90
+ run_time = htd(time.perf_counter() - self.start_time)
91
+ if sys.platform == 'win32':
92
+ mem_peack = bytes2human(process.memory_info().peak_wset)
93
+ else :
94
+ mem_peack = bytes2human(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 0.0009765625) # in bytes
95
+ return run_time, mem_peack
96
+
97
+ def _print(self, toprint, end='\n'):
98
+ if self.logger:
99
+ self.logger(toprint)
100
+ else:
101
+ print(toprint, end=end)
102
+
103
+ def print_line(self, fname, delta_time, delta_mem, end='\n', color=""):
104
+ delta_mem = " Δ " + f"{delta_mem:>7}"
105
+ if fname in self.profThread.keys():
106
+ mmax = 0.
107
+ if fname in self.profData.keys():
108
+ mmax = max(self.profData[fname]["dm_list"])
109
+ if mmax < self.profThread[fname] :
110
+ mmax = self.profThread[fname]
111
+ delta_mem += " / peak " + f"{bytes2human(mmax):>7}"
112
+ toprint = f"{color} ⚡ {fname: ^21} took : {delta_time:<10} consumes : {delta_mem} \033[0m"
113
+ self._print(toprint, end)
114
+
115
+ def __str__(self):
116
+ run_time, mem_peack = self.get_global_info()
117
+ str = ("\n " + "⚡" * 6 + f" customProfiler log : global timer {run_time} / max memory use {mem_peack:^10}"+ "⚡" * 6)
118
+ str += "\n " + "⚡" * 50
119
+ str += "\n ⚡ {:^31} | {:8} | {:<29} | {:^17} ⚡".format("fct name"
120
+ , "Nb call"
121
+ , " time : mean / global"
122
+ , "mem : mean / max")
123
+ str += "\n ⚡ "+ "="*95 + "⚡"
124
+ for key, val in self.profData.items():
125
+ t_str = htd(val["dt"])
126
+ t_p_call_str = htd(val["dt"]/val['nbCall'])
127
+ str += f"\n ⚡ {key: ^31.31} | {val['nbCall']:^8} "
128
+ str += f"| {t_p_call_str} / {t_str} "
129
+ strmen = bytes2human(self.profData[key]["dm"]/val['nbCall'])
130
+ mmax = max(self.profData[key]["dm_list"])
131
+ if key in self.profThread.keys():
132
+ if mmax < self.profThread[key] :
133
+ mmax = self.profThread[key]
134
+ strmaxmem = bytes2human(mmax)
135
+ str += f"| {strmen:>7} / {strmaxmem:>7} ⚡"
136
+ str += "\n " + "⚡" * 50
137
+ return str
138
+
139
+ def __del__(self):
140
+ print(self)
@@ -0,0 +1,63 @@
1
+ import logging
2
+
3
+ #https://stackoverflow.com/questions/2183233/how-to-add-a-custom-loglevel-to-pythons-logging-facility/35804945#35804945
4
+ def add_logging_level(levelName, levelNum, methodName=None):
5
+ """
6
+ Comprehensively adds a new logging level to the `logging` module and the
7
+ currently configured logging class.
8
+
9
+ `levelName` becomes an attribute of the `logging` module with the value
10
+ `levelNum`. `methodName` becomes a convenience method for both `logging`
11
+ itself and the class returned by `logging.getLoggerClass()` (usually just
12
+ `logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
13
+ used.
14
+
15
+ To avoid accidental clobberings of existing attributes, this method will
16
+ raise an `AttributeError` if the level name is already an attribute of the
17
+ `logging` module or if the method name is already present
18
+
19
+ Example
20
+ -------
21
+ >>> addLoggingLevel('TRACE', logging.DEBUG - 5)
22
+ >>> logging.getLogger(__name__).setLevel("TRACE")
23
+ >>> logging.getLogger(__name__).trace('that worked')
24
+ >>> logging.trace('so did this')
25
+ >>> logging.TRACE
26
+ 5
27
+
28
+ """
29
+ if not methodName:
30
+ methodName = levelName.lower()
31
+
32
+ if hasattr(logging, levelName):
33
+ raise AttributeError('{} already defined in logging module'.format(levelName))
34
+ if hasattr(logging, methodName):
35
+ raise AttributeError('{} already defined in logging module'.format(methodName))
36
+ if hasattr(logging.getLoggerClass(), methodName):
37
+ raise AttributeError('{} already defined in logger class'.format(methodName))
38
+
39
+ # This method was inspired by the answers to Stack Overflow post
40
+ # http://stackoverflow.com/q/2183233/2988730, especially
41
+ # http://stackoverflow.com/a/13638084/2988730
42
+ def logForLevel(self, message, *args, **kwargs):
43
+ if self.isEnabledFor(levelNum):
44
+ self._log(levelNum, message, args, **kwargs)
45
+ def logToRoot(message, *args, **kwargs):
46
+ logging.log(levelNum, message, *args, **kwargs)
47
+
48
+ logging.addLevelName(levelNum, levelName)
49
+ setattr(logging, levelName, levelNum)
50
+ setattr(logging.getLoggerClass(), methodName, logForLevel)
51
+ setattr(logging, methodName, logToRoot)
52
+
53
+ if __name__ == "__main__":
54
+
55
+ add_logging_level('PROFILER', 25)
56
+ logging.getLogger().setLevel("PROFILER")
57
+ logger = logging.getLogger("⚡")
58
+
59
+ ch = logging.StreamHandler()
60
+ logger.addHandler(ch)
61
+
62
+ logger.profiler("test print")
63
+
@@ -0,0 +1,95 @@
1
+ import sys
2
+ import time
3
+
4
+ from functools import wraps
5
+
6
+ import threading
7
+ from threading import Thread
8
+ from threading import Event
9
+
10
+ import psutil
11
+ from psutil._common import bytes2human
12
+ process = psutil.Process()
13
+
14
+ from custom_profiler.line_by_line import trace_calls
15
+ from custom_profiler.collecteur import profiler_collecteur, INTERACTIVITY_OPT_ENUM
16
+ from custom_profiler.human_readable_time import human_time_duration as htd
17
+
18
+
19
+ profC = profiler_collecteur()
20
+
21
+
22
+ def task(event, fname, start_time, start_mem):
23
+ i = 0
24
+ while True :
25
+ time.sleep(0.01)
26
+ if i % 100 == True :
27
+ t_str = htd(time.perf_counter() - start_time)
28
+ dm = process.memory_info().rss - start_mem
29
+ profC.thread_view(fname, dm) #sauvegarde delta mem max
30
+ strmen = bytes2human(dm)
31
+ if profC.interractivity == INTERACTIVITY_OPT_ENUM.ENABLE :
32
+ if threading.active_count() < 3:
33
+ profC.print_line(fname, t_str, strmen, end="\r", color="\033[93m")
34
+
35
+ i += 1
36
+ if event.is_set():
37
+ break
38
+
39
+
40
+ class thread_mananger:
41
+ def __init__(self, fname, start_time, start_mem):
42
+ self.event = Event()
43
+ self.t = Thread(target=task, args=(self.event, fname, start_time, start_mem))
44
+ self.t.daemon = True
45
+ self.t.start()
46
+
47
+ def end(self):
48
+ self.event.set()
49
+ self.t.join()
50
+
51
+ #https://stackoverflow.com/questions/5929107/decorators-with-parameters
52
+ def profiler(func, linePerline):
53
+ @wraps(func)
54
+ def wrapper(*args, **kwargs):
55
+ if profC.interractivity != INTERACTIVITY_OPT_ENUM.DISABLE and linePerline == False:
56
+ tm = thread_mananger(func.__name__, time.perf_counter(), process.memory_info().rss)
57
+
58
+ start_mem = process.memory_info().rss
59
+ start_time = time.perf_counter()
60
+
61
+ if linePerline :
62
+ sys.settrace(trace_calls)
63
+ result = func(*args, **kwargs)
64
+ if linePerline :
65
+ sys.settrace(None)
66
+
67
+ end_time = time.perf_counter()
68
+ end_mem = process.memory_info().rss
69
+
70
+ if profC.interractivity != INTERACTIVITY_OPT_ENUM.DISABLE and linePerline == False :
71
+ tm.end()
72
+
73
+ profC.save(func.__name__, end_time - start_time, end_mem - start_mem)
74
+
75
+ return result
76
+ return wrapper
77
+
78
+
79
+ class magic_profiler():
80
+
81
+ def __init__(self, func_name):
82
+ self.func_name = func_name
83
+
84
+ def __enter__(self):
85
+ if profC.interractivity != INTERACTIVITY_OPT_ENUM.DISABLE :
86
+ self.tm = thread_mananger(self.func_name, time.perf_counter(), process.memory_info().rss)
87
+ self.start_mem = process.memory_info().rss
88
+ self.start_time = time.perf_counter()
89
+
90
+ def __exit__(self, exc_type, exc_val, exc_tb):
91
+ end_time = time.perf_counter()
92
+ end_mem = process.memory_info().rss
93
+ if profC.interractivity != INTERACTIVITY_OPT_ENUM.DISABLE :
94
+ self.tm.end()
95
+ profC.save(self.func_name, end_time - self.start_time, end_mem - self.start_mem)
@@ -0,0 +1,49 @@
1
+ import time
2
+ from decimal import Decimal
3
+
4
+
5
+ TIME_UTS_DURATION_UNITS = (
6
+ ('d', 60*60*24),
7
+ ('h', 60*60),
8
+ ('min', 60),
9
+ ('s', 1),
10
+ )
11
+
12
+
13
+ TIME_LTS_DURATION_UNITS = (
14
+ ('s ', 1),
15
+ ('ms', 1e-3),
16
+ ('µs', 1e-6),
17
+ ('ns', 1e-9),
18
+ )
19
+
20
+
21
+ def human_time_duration(seconds):
22
+ assert type(seconds) in [float, int], f'seconds ({type(seconds)}) must be a float or int'
23
+ if seconds == 0:
24
+ return 0
25
+ if seconds <= 60 :
26
+ for unit, div in TIME_LTS_DURATION_UNITS:
27
+ if seconds >= div :
28
+ return f"{seconds/div:11.2f}{unit}"
29
+ return f"{Decimal(seconds):11.2E}s "
30
+ else :
31
+ if seconds < 3600 :
32
+ return f"{seconds//60:6.0f}min{seconds%60:2.0f}s"
33
+ else :
34
+ h = seconds // 3600
35
+ m = (seconds - h * 3600) // 60
36
+ s = seconds - (h * 3600) - (m * 60)
37
+ return f"{h:3}h{m:2}min{s:2}s"
38
+
39
+ if __name__ == "__main__":
40
+
41
+ for i in range(0, 11):
42
+ print(f"{10**-i:8}", human_time_duration(10**-i) \
43
+ , human_time_duration(2*10**-i)
44
+ , human_time_duration(10**-i - 10**(-i-3))
45
+ , human_time_duration(1/3*10**-i))
46
+
47
+ for i in [1, 2, 59, 60, 61, 144, 3599, 3600, 3601, 3660, 3661, 3600*120]:
48
+ print(f"{i:8}",human_time_duration(i))
49
+
@@ -0,0 +1,75 @@
1
+ import inspect
2
+ import time
3
+ import sys
4
+
5
+ import psutil
6
+ from psutil._common import bytes2human
7
+ process = psutil.Process()
8
+
9
+ from custom_profiler.collecteur import profiler_collecteur
10
+ from custom_profiler.human_readable_time import human_time_duration as htd
11
+
12
+ profC = profiler_collecteur()
13
+
14
+
15
+ def trace_lines(frame, event, arg):
16
+
17
+ code_source, lineStart = inspect.getsourcelines(frame.f_code)
18
+ co_name = frame.f_code.co_name
19
+
20
+ if event != "line":
21
+ fname = f"l {frame.f_lineno:<3} {profC.__src[-1]:40}"
22
+ fnameSave = f"{co_name} l {frame.f_lineno:<3}"
23
+ t = time.perf_counter() - profC.__tic
24
+ men = process.memory_info().rss - profC.__tic_mem
25
+ profC.save(fnameSave, t, men, fname)
26
+ profC._print( " " + "⚡"*20 + f" line per line : end")
27
+ return
28
+
29
+ profC.__src.append(code_source[frame.f_lineno - lineStart].split('\n')[0])
30
+
31
+ if len(profC.__src) == 1 :
32
+ head = code_source[1].split('\n')[0]
33
+ profC._print( " " + "⚡"*20 + f" line per line : {head.split()[1].split('(')[0]} from {frame.f_code.co_filename}")
34
+ else :
35
+ fname = f"l {frame.f_lineno-1:<3} {profC.__src[-2]:40}"
36
+ fnameSave = f"{co_name} l {frame.f_lineno-1:<3}"
37
+ t = time.perf_counter() - profC.__tic
38
+ men = process.memory_info().rss - profC.__tic_mem
39
+ profC.save(fnameSave, t, men, fname)
40
+
41
+ profC.__tic = time.perf_counter()
42
+ profC.__tic_mem = process.memory_info().rss
43
+
44
+
45
+ def trace_calls(frame, event, arg):
46
+ profC.__tic = 0.
47
+ profC.__tic_mem = 0.
48
+ profC.__src = []
49
+
50
+ if event != "call":
51
+ return
52
+ return trace_lines
53
+
54
+
55
+ def lpl(fonction):
56
+ def wrapper(*args, **kwargs):
57
+ sys.settrace(trace_calls)
58
+ resultat = fonction(*args, **kwargs)
59
+ sys.settrace(None)
60
+ return resultat
61
+ return wrapper
62
+
63
+
64
+ if __name__ == "__main__":
65
+
66
+ @lpl
67
+ def my_func():
68
+ a = [1] * (10 ** 6)
69
+ b = [2] * (2 * 10 ** 7)
70
+ time.sleep(1)
71
+ del b
72
+ # time.sleep(5)
73
+ return a
74
+
75
+ my_func()
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.1
2
+ Name: custom_profiler
3
+ Version: 0.1.0
4
+ Summary: time and memory profiler
5
+ Author: Karim Ammar
6
+ Author-email: karim.ammar@cea.fr
7
+ Maintainer: Karim Ammar
8
+ Keywords: profiler
9
+ Requires-Dist: psutil
10
+
11
+ time and memory profiler
@@ -0,0 +1,10 @@
1
+ custom_profiler/__init__.py,sha256=uZ_u96uNulrFnkRWXzk8kLYC1QMU3U_Zn1nC6eOA2c8,378
2
+ custom_profiler/collecteur.py,sha256=LhwCfbIA7rP7izCQN1Ry4JISvzHRQj-CXuXtOfMho8o,5967
3
+ custom_profiler/custum_logger.py,sha256=x6AV6PI3XNFszhPdJ08BynRhF5R5tJFaWvB09PvM-Dw,2450
4
+ custom_profiler/custum_profiler.py,sha256=nCDIATRqKm2-AzqeAeY0yhJwYfbVmLYqOsBzzgkpqpM,3103
5
+ custom_profiler/human_readable_time.py,sha256=xtYYVqfdbhag8L1ajhCjv8tq1YDxZEoq9a9ck_voVqM,1291
6
+ custom_profiler/line_by_line.py,sha256=MOSBGhrvStWm0PC-ByNbxKfkR9OwW6QVaC-c_wlNX64,2043
7
+ custom_profiler-0.1.0.dist-info/METADATA,sha256=CnpVw6SFKVoXdtaLlFBiJFkCG90UFzxXP2Kh25veJuI,237
8
+ custom_profiler-0.1.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
9
+ custom_profiler-0.1.0.dist-info/top_level.txt,sha256=ublWGGbVR2ckXpSymkDluEn2-sP8i6LVpql4DZOs1YY,16
10
+ custom_profiler-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (70.2.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ custom_profiler