Salome HOME
9fd0308f8088e21f9afb4a4ac65476a27e174db9
[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   def stack_(frame):
80     framelist = []
81     while frame:
82       framelist.append(frame)
83       frame = frame.f_back
84     return framelist
85
86   stack = stack_(sys._getframe(1))
87   start = 0 + skip
88   if len(stack) < start + 1:
89     return ''
90   parentframe = stack[start]
91
92   name = []
93   module = inspect.getmodule(parentframe)
94   # `modname` can be None when frame is executed directly in console
95   # TODO(techtonik): consider using __main__
96   if module:
97     name.append(module.__name__)
98   # detect classname
99   if 'self' in parentframe.f_locals:
100     # I don't know any way to detect call from the object method
101     # XXX: there seems to be no way to detect static method call - it will
102     #      be just a function call
103     name.append(parentframe.f_locals['self'].__class__.__name__)
104   codename = parentframe.f_code.co_name
105
106   fr = inspect.currentframe().f_back
107   lineno = [fr.f_lineno]
108   for i in range(20):  # no more 20 for precaution
109     fr = fr.f_back
110     if fr is None:
111       break
112     #print("*** frame locals %s" % str(fr.f_locals.keys()))
113     #print("*** frame globals %s" % str(fr.f_globals.keys()))
114     try:
115       namesrc = fr.f_globals["__name__"]
116       if namesrc == "__main__":
117         namesrc = os.path.basename(fr.f_globals["__file__"])
118       lineno.insert(0, (namesrc + "[%s]" % fr.f_lineno))
119     except:
120       lineno.insert(0, ("??", fr.f_lineno))
121
122   if codename != '<module>':  # top level usually
123     name.append(codename)  # function or a method
124
125   #print("lineno", lineno)
126   #print("name", name)
127
128   name[-1] += " // STACK: %s" % " ".join(lineno[0:-1])
129
130   del parentframe
131   return ".".join(name)
132
133
134 ##############################################################################
135 def example_of_use(toCall):
136   """
137   example of use caller_name_simple, or else
138   """
139   class Dummy:
140     def one_method(self):
141       print("4- call in class %s" % toCall(0))
142
143   print("1- call in %s" % toCall(0)) # output from main to here
144   print("2- call in %s" % toCall(0))
145   print("3- call in %s" % toCall(1)) # output from main to caller
146   tmp = Dummy()
147   tmp.one_method()
148
149
150 ##############################################################################
151 # main as an example
152 ##############################################################################
153 if __name__ == "__main__":
154   example_of_use(caller_name_simple)
155   example_of_use(caller_name_stack)
156
157 """
158 example of output
159
160 1- call in __main__.example_of_use[143]
161 2- call in __main__.example_of_use[144]
162 3- call in __main__[154]
163 4- call in class __main__.Dummy.one_method[141]
164 1- call in __main__.example_of_use // STACK: callerName.py[155]
165 2- call in __main__.example_of_use // STACK: callerName.py[155]
166 3- call in __main__ // STACK: callerName.py[155]
167 4- call in class __main__.Dummy.one_method // STACK: callerName.py[155] callerName.py[147]
168 """
169
170
171 # here default caller_name is user choice...
172 caller_name = caller_name_simple     # not so verbose
173 # caller_name = caller_name_stack    # more verbose, with stack