Verwendung cProfile für Darstellung von call graphs in ePyDo

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

Hallo liebes Python Forum,

Ich habe ein riesiges Proramm "vermacht bekommen", natürlich nicht dokumentiert. Also bin ich jetzt dabei es mithilfe von ePyDoc zu dokumentieren, um es später weiter entwickeln zu können.

Mein Problem:
ePyDoc erzeugt beim Aufruf folgende Fehlermeldung: "No profiling information for call graph!"
Dieser Fehler wird erzeugt, weil ich in meiner API auch call graph für Funktionen anzeigen lassen möchte.
Also habe ich zwei Tage lang verzweifelt versucht einen cProfile.run()-Aufruf zu verwenden, leider ohne Erfolg.
Aus der ePyDoc Dokumentation werde ich leider auch nicht schlau.

Mein Frage:
Kann mir jemand von Euch einen einfachen Quellcode bereitstellen, der von ePyDoc korrekt erfasst wird und dessen Funktionen in ePyDoc in Form von "call graph"-Bildern dargestellt werden? Etwas ganz einfaches würde völlig ausreichen, nur damit ich endlich einmal sehe, wie man das macht.

Vielen Dank jedem, der mir helfen kann.

Gruß
python_bunny
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Willkommen im Forum.

Also, zunächst brauchst du http://www.graphviz.org/. Dann musst du dir eine Python Shell schnappen und cProfile importieren. Nun bei "cProfile.run()" zwei Parameter übergeben. Der erste ist dein zu testender Funktionsaufruf als String und der zweite ist der Dateipfad wo du die Profiler-Datei speichern möchtest. Danach brauchst du nur beim Aufruf von epydoc noch "--pstat" angeben und dort deine Datei als Parameter angeben. Der Rest passiert automatisch.
Ein Beispielaufruf könnte so aussehen:

Code: Alles auswählen

epydoc --html --graph callgraph --dotpath /usr/bin/dot --pstat profile_file --output api/html python_modul
und Quellcode findest du hier überall :wink:

Edit: Hier ein kleines Test Beispiel:
Edit2: Nochmal etwas hübscher gemacht und man muss nur noch den Pythonscript ausführen.

Code: Alles auswählen

import cProfile
import os
import subprocess
import webbrowser

def add(num1, num2):
    """Addiert zwei Zahlen und wandelt das Ergebnis in einen Integer-Wert.

    @type num1: C{int, float}
    @parameter num1: Zahl Nummer 1

    @type num2: C{int, float}
    @parameter num2: Zahl Nummer 2

    @returntype: C{int}
    @return: Die Summe beider Zahlen.
    """    
    return int(num1 + num2)

def sub(num1, num2):
    """Subtrahiert zwei Zahlen und wandelt das Ergebnis in einen Integer-Wert.

    @type num1: C{int, float}
    @parameter num1: Zahl Nummer 1

    @type num2: C{int, float}
    @parameter num2: Zahl Nummer 2

    @returntype: C{int}
    @return: Die Differenz beider Zahlen.
    """
    return add(num1, -num2)
    
if __name__ == "__main__":
    modulpath = os.path.abspath(__file__)
    workpath = os.path.dirname(modulpath)
    docpath = os.path.join(workpath, "api", "html")
    filename = os.path.join(workpath, "file.profile")
    
    cProfile.run("sub(1, 2)", filename)

    if not os.path.exists(docpath):
        os.makedirs(docpath)

    command = ["epydoc", "--html", "--graph", "callgraph", "--pstat",
               filename, "--output", docpath, modulpath]

    process = subprocess.Popen(command, shell=True)
    process.wait()
    webbrowser.open(os.path.join(docpath, "index.html"))
Zuletzt geändert von Xynon1 am Mittwoch 20. April 2011, 15:36, insgesamt 1-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

Vielen Dank Xynon1,

leider kann ich immer noch keine CallGraphen in meiner API darstellen, aber ich habe das Gefühl, dass ich kurz davor bin! Bitte hilf mir noch ein klein wenig, ich bin kurz davor! Hier mein Vorgehen in 3 Schritten:

v1. Ich habe deinen Quellcode übernommen und ihn als "test.py" gespeichert.
v2. Ich habe nur folgenden Teil aus Deinem Quellcode herausgenommen, weil ich diesen Aufruf in einer command shell außerhalb des Quellcodes ausführen möchte:

Code: Alles auswählen

  command = ["epydoc", "--html", "--graph", "callgraph", "--dotpath",
                "/usr/bin/dot", "--pstat", filename, "--output", docpath,
                modulname]
    process = subprocess.Popen(command, shell=True)
    process.wait()
    webbrowser.open(os.path.join(docpath, "index.html"))
v3. Stattdessen, führe ich folgenden Aufruf in meiner command shell aus:
"Python epydoc.py --html -- graph callgraph --dotpath /Programme/Graphviz2.26.3/bin/dot.exe --pstat profile_file test.py".

Hierzu folgende Informationen:
i1. Das Programm epydoc.py liegt im gleichen Verzeichnis wie test.py.
i2. Mein Graphviz Programm hat den Pfad /Programme/Graphviz2.26.3/bin/dot.exe auf der gleichen Festplatte wie sich auch test.py befindet.

Das Ergebnis:
e1. Die HTML API wird ohne Fehlermeldung erstellt
e2. Die HTML APi enthält aber keine Graphen (callgraph)

Um nun einen Graphen für die Funktion "add()" zu erzeugen, bin ich weiterhin so vorgegangen:

v4. Hinzufügen des Befehls G{callgraph} im Quellcode:

Code: Alles auswählen

 def add(num1, num2):
    """Addiert zwei Zahlen und wandelt das Ergebnis in einen Integer-Wert.
    
    G{callgraph}
    @type num1: C{int, float}
    @parameter num1: Zahl Nummer 1

    @type num2: C{int, float}
    @parameter num2: Zahl Nummer 2

    @returntype: C{int}
    @return: Die Summe beider Zahlen.
    """    
    return int(num1 + num2)
Das Ergebnis:
e3. Die HTML API wird ohne Fehlermeldung erstellt
e4. Die HTML APi enthält aber keine Graphen (callgraph). Im Gegensatz zu vorhin enthält sie nun ein leeres Rechteck mit der Unterschrift "Call Graph for add".

Ich glaube, ich bin ziemlich nah dran. Was mache ich noch falsch?

Vielen Dank
BlackJack

@python_bunny: Wenn Du den Quelltext von Xynon1 so übernommen hast, dann wird für `add()` ja auch keine Profiler-Information aufgezeichnet sondern für `sub()`.
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

@BlackJack: OK, ich habe den Quelltext also folgendermaßen geändert:

Code: Alles auswählen

def sub(num1, num2):
    """Subtrahiert zwei Zahlen und wandelt das Ergebnis in einen Integer-Wert.

    G{callgraph}
    @type num1: C{int, float}
    @parameter num1: Zahl Nummer 1


    @type num2: C{int, float}
    @parameter num2: Zahl Nummer 2

    @returntype: C{int}
    @return: Die Differenz beider Zahlen.
    """
    return add(num1, -num2)
Wenn ich Dich richtig verstehe, dann müsste jetzt für die Funktion sub() ein callgrpah entstehen in der API. Leider ist nun in der API stattdessen bei der Beschreibung der Funktion sub() ein leeres Rechteck mit der Unterschrift "Call Graph for sub" ... Woran kann es jetzt noch liegen?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Nein, das G{callgraph} wird nur bei manueller Einstellung benötigt. Es sollte allein schon automatisch für jede Funktion die ähm, "profiled" wurde ein callgraph angelegt werden.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

@ Xynon1: Also, ich habe jetzt das "G{callgraph}" rausgeschmissen und mein Quelltext sieht folgendermaßen aus:

Code: Alles auswählen

import cProfile
import os
import subprocess
import webbrowser

def add(num1, num2):
    """Addiert zwei Zahlen und wandelt das Ergebnis in einen Integer-Wert.
    
    @type num1: C{int, float}
    @parameter num1: Zahl Nummer 1

    @type num2: C{int, float}
    @parameter num2: Zahl Nummer 2

    @returntype: C{int}
    @return: Die Summe beider Zahlen.
    """    
    return int(num1 + num2)

def sub(num1, num2):
    """Subtrahiert zwei Zahlen und wandelt das Ergebnis in einen Integer-Wert.

    @type num1: C{int, float}
    @parameter num1: Zahl Nummer 1


    @type num2: C{int, float}
    @parameter num2: Zahl Nummer 2

    @returntype: C{int}
    @return: Die Differenz beider Zahlen.
    """
    return add(num1, -num2)
   
if __name__ == "__main__":
    workpath = os.path.dirname(os.path.abspath(__file__))
    docpath = os.path.join(workpath, "api", "html")
    filename = os.path.join(workpath, "profile_file")
    modulname = os.path.basename(__file__)
   
    cProfile.run("sub(1, 2)", filename)

    if not os.path.exists(docpath):
        os.makedirs(docpath)
Der Funktionsaufruf war in der command shell:
""Python epydoc.py --html -- graph callgraph --dotpath /Programme/Graphviz2.26.3/bin/dot.exe --pstat profile_file test.py""

Leider werden jetzt in der API nicht einmal mehr die leeren Rechtecke dargestellt. Die API erscheint ohne jegliche Graphen. Ich verwende folgende Versionen:
- ePyDoc: Version 3.0
- GraphViz: Version 2.26.3

Was könnte ich noch vergessen haben?
Danke für Eure Hilfe!
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ok, ich habe den Fehler gefunden.

epydoc benötigt in der Profile-Datei die absoluten Pfade der Module, diese werden jedoch nur erstellt wenn man es von einem anderen Ort als dem lokalen Verzeichnis ausführt.

Habe den Quellcode oben mal angepasst, probier mal aus der Windows-Konsole das ganze etwa so zu starten:

Code: Alles auswählen

C:\Dokumente und Einstellungen\User>C:\Python26\python.exe D:\test\test.py
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

@Xynon:
Habe den Quellcode oben mal angepasst...
Wolltest Du den abgepassten Quellcode auch noch darstellen oder nicht?

Gruß
python_bunny
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

@ Xynon1: Chakaaaaaaaa! Juhuuuuuuuuuu!

Endlich ein Graph in meiner API Dokumentation.
Vielen Dank, da sieht man, dass Du einfach ein alter Programmierhase bist :)

Für alle, die ein ähnliches Problem haben und auf diesen threat stoßen:

Die LÖSUNG des Problems war, dass man den absoluten Pfad der "profile_file" Datei angeben musste!

Dann wird die Datei von ePyDoc problemlos gefunden und die darin enthaltenen Informationen über die Funktionsaufrufe in der API dargestellt. Leider erfolgt, wenn man den absoluten Pfad nicht angibt, keine Fehlermeldung, sodass ich nicht auf diese Möglichkeit gekommen bin. Vielleicht schreibe ich den Entwicklern von ePyDoc eine e-mail, dass sie die Fehlermeldung eventuell noch ausgeben, das hätte mir enorm viel Zeit gespart.

Vielen Dank an Xynon1,
Deine Hilfe ist Gold wert!!!

--> Leider war es das dann doch nicht. Wen es interessiert, bitte einfach weiterlesen :)
Zuletzt geändert von python_bunny am Donnerstag 21. April 2011, 18:17, insgesamt 1-mal geändert.
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

@ Xynon1:
Hi, meine gestrige Freude hat nicht lange gewährt. Von einem Moment auf den anderen, funktioniert der gestrige Ansatz nicht mehr. Habe nun heute erneut 11 Stunden lang versucht, Graphen in meine API zu kriegen ... vergebens :( Hatte extra um 6:00h angefangen, um noch viel zu erledigen ...

Wenn ich Deinen Quellcode ausführe, dann wird die Profile-Datei erstellt, es werden die Ordner /api/html erstellt. Aber dann erscheint die Fehlermeldung: "Das System kann die angegebene Datei nicht finden: C:\Neu\api\html\index.html"

Und wenn ich den folgenden Befehl ausführe:

"C:\Neu\Python epydoc.py --html --graph callgraph --dotpath C:\Programme\Graphviz2.26.3\bin\dot.exe --pstat C.\Neu\file.profile C:\Neu\t.py" dann erzeugt er zwar eine hübsche API, aber ohne Graphen.

Ich bin momentan ein wenig niedergeschlagen, was mache ich nur falsch?

Danke für Hilfe
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Du musst nicht epydoc.py aufrufen sondern nur "epydoc".
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

Hi,

also ich versuche im Folgenden zu zeigen, was ich mache, damit mir jemand weiterhelfen kann.

Hier mein Quellcode:

Code: Alles auswählen

import cProfile

def a(v1,v2):
    """Addition

    @type v1: C{int}
    @parameter v1: Zahl
    
    @type v2: C{int}
    @parameter v2: Zahl

    @returntype: C{int}
    @return: Summe beider Zahlen.
    """    
    ergebnis = v1 + v2
    return ergebnis

def foo(v1,v2):
    """Produkt

    @type v1: C{int}
    @parameter v1: Zahl
    
    @type v2: C{int}
    @parameter v2: Zahl

    @returntype: C{int}
    @return: Produkt.
    """    
    ergebnis = a(v1,v2)*(v2)
    return ergebnis
   
if __name__ == "__main__":
    cProfile.run('foo(2,4)', 'fooproof')
In Anlehnung an die python Doku im Internet habe ich die zweie Funktion "foo()" genannt und die Datei, in der das Profil für dieses Programm gespeichert wird heißt "fooproof" (auch in Anlehnung an die python Doku).

1. Ich öffne eine Eingabeaufforderung
2. Ich gehe in das Verzeichnis, in dem sich das Programm "t.py" befindet (Der Quellcode oben gehört zu diesem Programm)
3. Ich gebe ein "Python -m epydoc --html t.py" --> Alles OK, eine API wird erstellt.

Nun möchte ich das ganze natürlich auch mit Graphen haben. Also gebe ich Folgendes ein:

4. "Python -m epydoc --html --graph callgraph t.py" --> Es erscheint folgende Fehlermeldung: "callgraph type may only be used if one or more pstat files are specified". Daher ändere ich meine Eingabe, wie es im 5. Schritt gezeigt ist:

5. "Python -m epydoc --html --pstat fooproof --graph callgraph t.py" --> Erneut wird eine API erstellt, aber ohne Graphen. Nun versuche ich, die Datei fooproof mit ihrem vollständigen Pfad anzugeben:

6. "Python -m epydoc --html --pstat C:\Neu\fooproof --graph callgraph t.py" --> Erneut wird eine API erstellt, aber ohne Graphen. Nun versuche ich, den Pfad des Zeichenprogrammes anzugeben, damit ePyDoc weiß, von wo aus gezeichnet wird:

7. "Python -m epydoc --html --pstat C:\Neu\fooproof --graph callgraph --dotpath \Programme\Graphviz2.26.3\bin\dot.exe t.py" --> Erneut wird eine API erstellt, aber ohne Graphen. Nun versuche ich letztlich auch das Programm selber mit einem vollständigen Pfad anzugeben:

8. "Python -m epydoc --html --pstat C:\Neu\fooproof --graph callgraph --dotpath \Programme\Graphviz2.26.3\bin\dot.exe C:\Neu\t.py" --> Erneut wird eine API erstellt, aber ohne Graphen.

Nun weiß ich leider nicht mehr weiter. Was ich bereits herausgefunden habe ist, dass ich die 'fooproof' - Datei auch mit folgendem Befehl in der Eingabeaufforderung erstellen kann:

"Python -m cProfile -o fooproof t.py" --> Leider ändert das nichts daran, dass ich die Graphen nicht in die API kriege ...

Wer weiß hierzu etwas?

Danke im Vorraus
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

@ Xynon: Ich habe Deinen Quellcode wirklich 1:1 ausprobiert, und er macht keine Graphen. ich habe draufhin in Deinem Quellcode die erzeugten Pfade durch fixe volsständige Pfade ersetzt -> ging auch nicht :K

Ich habe mittlerweile auch

Code: Alles auswählen

p = pstats.Stats('fooprof')
im Quellcode ausprobiert.
Daraufhin dann auch

Code: Alles auswählen

p.print_callers()
p.print_callees()((
Probiert habe ich auch

Code: Alles auswählen

p.add('fooprof')
Leider alles ohne Erfolg.

Ich kämpfe hart, aber ich finde die Ursache einfach nicht...

Danke für Vorschläge
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

:roll: - Du machst es dir viel zu kompliziert. (Zugegeben mein Script oben war etwas erschlagend)

Nochmal Schritt für Schritt.
1. Man erstelle sein Pythonscript, sagen wir unter C:\Neu mit dem Namen t.py
1.1 Der Inhalt sind die zwei Funktionen und folgender Inhalt.

Code: Alles auswählen

import cProfile
cProfile.run("foo(2,3)", r"C:\Neu\file.profile")
2. Man erstelle sich eine Profiler-Datei
2.1. Dazu nehme man eine Dos-Shell zur Hand.
2.2. Hier ist zu beachten, das man später die absoluten Pfade in der Profilerdatei benötigt, also vom Root-Pfad ausführen. (Heißt entweder direkt von C:\ bzw. / oder einer anderen Festplatte, dann werden die absoluten Pfade in die Profiler Datei geschrieben."
2.3 In der Konsole könnte das so aussehen "cd /d C:\" und dann "C:\Python\python.exe C:\Neu\t.py" - Jetzt sollte eine "file.profile"-Datei existieren und absolute Pfade enthalten.

3. epydoc mit den richtigen Parametern ausführen.
3.1 Wieder eine Dos-Konsole öffnen, dazu ist es diesmal egal in welchem Pfad man sich befindet, bequemer weise würde ich jetzt zu "cd /d C:\Neu" wechseln.
3.2 Hier muss noch der/die Ordner erstellt werden in den die Dokumentation soll. "mkdir api"
3.3 Dann einfach den Aufruf "epydoc --html t.py --graph callgraph --pstat file.profile --dotpath C:\Programme\Graphviz2.26.3\bin\dot.exe --output api"

4. Einfach in den Ordner C:\Neu\api gehen und die index.html Datei öffnen.

Edit:
Und mach deine Posts nicht immer mit unötigen Quelltexten voll, hier habe ich nur ein EDGE-Netz und ich muss eine Ewigkeit+1 warten damit er die vollständige Seite geladen hat :P
Zuletzt geändert von Xynon1 am Donnerstag 21. April 2011, 20:26, insgesamt 1-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

@ Xynon1: OK, bin gerade dabei, alles so auszuprobieren ... melde mich gleich wieder
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

@ Xynon1:
1. Erledigt.
2.1 Der Pfad sieht bei mir so aus: "C:\Programme\Python25\python.exe" ... es erscheint die Python-Eingabeaufforderung (">>>")
2.2 Verstehe ich nicht, gehört das noch zu 2.1? Es ist keine Aktion, die ich jetzt machen muss, oder?
2.3 Erledigt

3.1 Erledigt
3.2 Erledigt
3.3 Habe Folgendes eingetippt in der Python Eingabeaufforderung:
">>>epydoc --html t.py --graph callgraph --pstat file.profile --dotpath C:\Programme\Graphviz2.26.3\bin\dot.exe --output api
Ergebnis: "SytaxError: invalid syntax"

4. Ordner "api" ist (logischerweise) leer ...

Siehst Du, was ich bei 3.3 falsch mache?
python_bunny
User
Beiträge: 16
Registriert: Dienstag 19. April 2011, 12:46

Sag mal, kann es sein, dass es daran liegt, dass ich mein Python in "C:\Programme\Python25\python.exe" habe anstatt in in "C:\Python\python.exe"?

wenn ich nämlich in der Pythen Shell "epydoc" eingebe, dann kommt der Fehler "NameError: name epydoc is not defined"?

Die einzige Möglichkeit für mich, epydoc auszuführen ist, wenn ich "Python -m epydoc ... usw ..." eintippe, oder wenn ich die Datei "epydoc.py" in das Verzeichnis "C:\Neu\epydoc.py" kopiere. In diesen beiden Fällen funktioniert es, aber es wird kein Graph erzeugt :(

Ich bin jetzt ziemlich platt. Ich werde über die Feiertage noch daran arbeiten, weil meine Abschlussarbeit davon abhängt. Ich wünsche Dir jedenfalls schöne Osterfeiertage

Gruß
python_bunny
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ok, die Stelle hatte ich noch mal geändert gehabt, weil ich es direkt in der Shell umgesetzt hatte, in dem beschriebenen Fall brauchst du nur die normale Win-Konsole. Zudem 2.1 ist der wichtigste Schritt, ansonsten hast du am Ende keine Callgraphen.
Den Pfad zur Python.exe musst du selbst Eintragen, ich kenn dein System doch nicht, woher soll ich wissen wo und welche Version du von Python installiert hast. Unter meinem Windows sind die Python Versionen zB. unter E:\Python, also gebe ich den Pfad an der bei der Installation als default eingetragen ist. Welcher unter Win ebend C:\Python{Version}\python.exe ist. Allgemein solltes du dir angewöhnen das bei Hinweisen und Snippets mitdenken solltest und zu dem Schluss kommen, das absolute Pfade immer angepasst werden müssen.

Und nochmal klipp und klar, du brauchst epydoc nicht als Python-Modul, dafür wird bei der Installation automatisch eine Verknüpfung in den System Ordner von Windows erstellt, so das man ihn als Standard-Befehl wie "cd" oder "dir" nutzen kann. Kann man natürlich auch so machen wie du das machst, sollte Prinzipiell auch funktionieren, nur habe ich das nicht getestet.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten