Here is a simple tracing class and an example of what can be done with it.
instantiate the class at the beginning of each function you want to trace.
A decorator could also be done on the same principle but you would have to adapt the python frame parsing accordingly
class SequenceOn:
autonumber = True
init_done = False
def __init__(self,participant=""):
if not SequenceOn.init_done :
#activate if requested only once
if SequenceOn.autonumber:
print ("autonumber")
SequenceOn.init_done = True
#retrieve callee frame
callee_frame = sys._getframe(1)
#method/function name
self.__funcName = callee_frame.f_code.co_name
# look for a class name
if 'self' in callee_frame.f_locals:
self.__className = callee_frame.f_locals['self'].__class__.__name__
else:
self.__className = participant
#retrieve the caller frame and class name of the caller
caller_frame = sys._getframe(2)
if 'self' in caller_frame.f_locals:
self.__caller = caller_frame.f_locals['self'].__class__.__name__
else:
self.__caller = ""
#print the plantuml message
activate = "++" if self.__caller != self.__className else ""
print (f'{self.__caller} -> {self.__className} {activate} :{self.__funcName}')
def __del__(self):
''' print the return message upon destruction '''
if self.__caller != self.__className:
print (f'{self.__caller} <-- {self.__className} -- ')
def note(self,msg):
print (f'note over {self.__className}:{msg}')
class SequenceOff:
''' empty class allowing to disable the trace by eg doing in the begining of a file:
Seq = SequenceOn # enable sequence generation
Seq = SequenceOff # disable sequence generation
and using seq for tracing instead of SequenceOn
'''
def __init__(self,participant=""):
pass
def __call__(self,msg):
pass
def note(self,msg):
pass
On the below sample code:
if __name__ == "__main__":
class B:
def __init__(self):
pass
def funcB1(self):
s = SequenceOn()
def funcB2(self):
s = SequenceOn()
s.note("calling private method")
self.__funcB22()
def __funcB22(self):
s = SequenceOn()
class A:
def __init__(self):
pass
def funcA(self):
s = SequenceOn()
b = B()
b.funcB1()
b.funcB2()
# simple sequence
a = A()
a.funcA()
You get:
autonumber
-> A ++ :funcA
A -> B ++ :funcB1
A <-- B --
A -> B ++ :funcB2
note over B:calling private method
B -> B :__funcB22
A <-- B --
<-- A --
Which generates: