Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

It is possible to generate sequence diagram from python code?

I have about 700 lines of code. I have to write a little doc about this code. In this doc, I plan to have a sequence diagram to explain a bit more. something like this: enter image description here Since Iam a bit lazy, I would like to know if there is a tool to generate this diagram from code. I don't want to lose my entire weekend doing this.

Do some have an idea that could help me?

Thanks.

like image 467
dmx Avatar asked Jul 21 '17 12:07

dmx


3 Answers

The Plantuml in python could be helpful. For how to install use link PlantUML with python

Text file should be like so:

Python -> HL : create
activate HL
HL -> LL : run
activate LL
LL -> SLI : exec
activate SLI
SLI --> LL
deactivate SLI
LL --> HL
deactivate LL
HL -> LL : push
activate LL
LL -> SLI : push
activate SLI
SLI --> LL
deactivate SLI
LL --> HL
deactivate LL
HL -> LL : run
activate LL
LL -> SLI : exec
activate SLI
SLI --> LL
deactivate SLI
LL --> HL
deactivate LL
HL -> LL : pop
activate LL
LL -> SLI : pop
activate SLI
SLI --> LL
deactivate SLI
LL --> HL
deactivate LL
HL --> Python
deactivate HL

enter image description here

It is possible to generate the text by python for this purpose, for example, you can add after the method declaration.

print>>sd.txt, "->"+str(self.name)+":className"

This will append to sd.txt and finally create png file in the project directory by running:

python -m plantuml sd.txt

python-UML-sequence-diagram

like image 41
Ali Hallaji Avatar answered Sep 22 '22 14:09

Ali Hallaji


I was searching for this topic too and found the following usefull: https://github.com/bereal/pyspy

It patches the bytecode using byteplay and you can add your own callback-functions to do something before each call and after each. Similar to writting a profiler using sys.settrace but easier.

Well, I added some fixes as issue-comments because I was not able to use git properly from here. Your functions may write DLS as text, like is needed by PlantUML or ZenUML... and use it to generate your diagram.

like image 39
Mayra Delgado Avatar answered Sep 22 '22 14:09

Mayra Delgado


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: enter image description here

like image 118
Jean-Marc Volle Avatar answered Sep 19 '22 14:09

Jean-Marc Volle