Salome HOME
style: black format
[tools/sat.git] / src / callerName.py
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 # https://gist.github.com/techtonik/2151727
5 # Public Domain, i.e. feel free to copy/paste
6 # Considered a hack in Python 2
7
8 import os
9 import sys
10 import inspect
11 import logging
12 import pprint as PP
13
14
15 ##############################################################################
16 def caller_name_simple(skip=1):
17     """
18     Get a name of a caller in the format module.class.method
19
20     'skip' specifies how many levels of stack to skip while getting caller
21     name. skip=1 means 'who calls me', skip=2 'who calls my caller' etc.
22
23     An empty string is returned if skipped levels exceed stack height
24     """
25
26     def stack_(frame):
27         framelist = []
28         while frame:
29             framelist.append(frame)
30             frame = frame.f_back
31         return framelist
32
33     stack = stack_(sys._getframe(1))
34     start = 0 + skip
35     if len(stack) < start + 1:
36         return ""
37     parentframe = stack[start]
38
39     name = []
40     module = inspect.getmodule(parentframe)
41     # `modname` can be None when frame is executed directly in console
42     # TODO(techtonik): consider using __main__
43     if module:
44         name.append(module.__name__)
45     # detect classname
46     if "self" in parentframe.f_locals:
47         # I don't know any way to detect call from the object method
48         # XXX: there seems to be no way to detect static method call - it will
49         #      be just a function call
50         name.append(parentframe.f_locals["self"].__class__.__name__)
51     codename = parentframe.f_code.co_name
52
53     fr = inspect.currentframe().f_back
54     for i in range(skip):  # no more 20 for precaution
55         fr = fr.f_back
56         if fr is None:
57             break
58     lineno = fr.f_lineno
59
60     if codename != "<module>":  # top level usually
61         name.append(codename)
62
63     name[-1] += "[%s]" % str(lineno)  # function or a method
64     del parentframe
65     return ".".join(name)
66
67
68 ##############################################################################
69 def caller_name_stack(skip=1):
70     """
71     Get a name of a caller in the format module[no].class[no].method[no]
72     where [no] is line nunber in source file(s)
73
74     'skip' specifies how many levels of stack to skip while getting caller
75     name. skip=1 means 'who calls me', skip=2 'who calls my caller' etc.
76
77     An empty string is returned if skipped levels exceed stack height
78     """
79
80     def stack_(frame):
81         framelist = []
82         while frame:
83             framelist.append(frame)
84             frame = frame.f_back
85         return framelist
86
87     stack = stack_(sys._getframe(1))
88     start = 0 + skip
89     if len(stack) < start + 1:
90         return ""
91     parentframe = stack[start]
92
93     name = []
94     module = inspect.getmodule(parentframe)
95     # `modname` can be None when frame is executed directly in console
96     # TODO(techtonik): consider using __main__
97     if module:
98         name.append(module.__name__)
99     # detect classname
100     if "self" in parentframe.f_locals:
101         # I don't know any way to detect call from the object method
102         # XXX: there seems to be no way to detect static method call - it will
103         #      be just a function call
104         name.append(parentframe.f_locals["self"].__class__.__name__)
105     codename = parentframe.f_code.co_name
106
107     fr = inspect.currentframe().f_back
108     lineno = [fr.f_lineno]
109     for i in range(20):  # no more 20 for precaution
110         fr = fr.f_back
111         if fr is None:
112             break
113         # print("*** frame locals %s" % str(fr.f_locals.keys()))
114         # print("*** frame globals %s" % str(fr.f_globals.keys()))
115         try:
116             namesrc = fr.f_globals["__name__"]
117             if namesrc == "__main__":
118                 namesrc = os.path.basename(fr.f_globals["__file__"])
119             lineno.insert(0, (namesrc + "[%s]" % fr.f_lineno))
120         except:
121             lineno.insert(0, ("??", fr.f_lineno))
122
123     if codename != "<module>":  # top level usually
124         name.append(codename)  # function or a method
125
126     # print("lineno", lineno)
127     # print("name", name)
128
129     name[-1] += " // STACK: %s" % " ".join(lineno[0:-1])
130
131     del parentframe
132     return ".".join(name)
133
134
135 ##############################################################################
136 def example_of_use(toCall):
137     """
138     example of use caller_name_simple, or else
139     """
140
141     class Dummy:
142         def one_method(self):
143             print("4- call in class %s" % toCall(0))
144
145     print("1- call in %s" % toCall(0))  # output from main to here
146     print("2- call in %s" % toCall(0))
147     print("3- call in %s" % toCall(1))  # output from main to caller
148     tmp = Dummy()
149     tmp.one_method()
150
151
152 ##############################################################################
153 # main as an example
154 ##############################################################################
155 if __name__ == "__main__":
156     example_of_use(caller_name_simple)
157     example_of_use(caller_name_stack)
158
159 """
160 example of output
161
162 1- call in __main__.example_of_use[143]
163 2- call in __main__.example_of_use[144]
164 3- call in __main__[154]
165 4- call in class __main__.Dummy.one_method[141]
166 1- call in __main__.example_of_use // STACK: callerName.py[155]
167 2- call in __main__.example_of_use // STACK: callerName.py[155]
168 3- call in __main__ // STACK: callerName.py[155]
169 4- call in class __main__.Dummy.one_method // STACK: callerName.py[155] callerName.py[147]
170 """
171
172
173 # here default caller_name is user choice...
174 caller_name = caller_name_simple  # not so verbose
175 # caller_name = caller_name_stack    # more verbose, with stack