Seite 5 von 7
Re: Programm Kontrolle
Verfasst: Mittwoch 13. Juni 2012, 19:10
von Nobuddy
Nochmal kurz eine Rückmeldung, bevor ich mir die EM anschaue.
Hyperion, das mit den DOC-Strings ist nicht vergessen!
Dachte aber nicht, daß ich Euch erklären muß, die 'else:pass' Geschichte verstanden zu haben.
Solche Dinge sind eigentlich logisch, auch wenn man mich erst drauf schubsen mußte ...
BlackJack, 'ps -u' nicht korrekt ist, jetzt weiß ich auch wieder, warum ich am Ende noch '2>/dev/null' ran gehängt habe ....
'ps -u $USER' wäre die richtige Eingabe.
Habe mir das Paket python-psutil installiert, nur mit einer deutschen Beschreibung über psutil ist ziemlich mau.
Re: Programm Kontrolle
Verfasst: Donnerstag 14. Juni 2012, 09:53
von Leonidas
Nobuddy hat geschrieben:Habe mir das Paket python-psutil installiert, nur mit einer deutschen Beschreibung über psutil ist ziemlich mau.
Also dann die Englische nehmen oder in den Quelltext schauen. Denk nicht dass der so kompliziert ist, insbesondere wenn du dir nur den relevanten Teil zu deinem System anschaust und etwa den Windows-relevanten-Teil weglässt.
Re: Programm Kontrolle
Verfasst: Donnerstag 14. Juni 2012, 17:25
von Nobuddy
Hyperion, wie versprochen habe ich, das mit den DOC-Strings durchgearbeitet und habe auch dazu noch, hier
http://openbook.galileocomputing.de/pyt ... 13_003.htm und hier
http://abop-german.berlios.de/read/docstrings.html die Infos gelesen.
Jetzt habe ich auch Deinen berechtigten Einwand verstanden, daß eine Funktion nur für einen DOC-String nichts bringt.
Vielleicht wäre das ein Anwendungsfall ...
Code: Alles auswählen
def a():
'''Ich bin ein DOC-String und habe etwas zu sagen'''
# Hier kommen weitere Aufgaben
pass
def b():
'''Hier wird die Funktion b erklärt'''
# Hier kommen weitere Aufgaben
pass
def c():
'''Hier wird die Funktion c erklärt'''
# Hier kommen weitere Aufgaben
pass
def doc_ausgabe():
print 'Funktion a:'
print a.__doc__
print ''
print 'Funktion a:'
print b.__doc__
print ''
print 'Funktion a:'
print c.__doc__
doc_ausgabe()
Um DOC-Strings anzuzeigen, kann die Ausgabe per print-Anweisung erfolgen.
Für die Ausgabe benötigt man den Namen der Klasse bzw. der Funktion, in der der DOC-String enthalten ist und lässt sich dies dann mit z.B.:
aus. Möchte man aus einem anderen Modul heraus einen DOC-String sich ausgeben lassen, so kann man dies so erreichen:
Code: Alles auswählen
from beispielmodul import meine Funktion
print(meineFunktion.__doc__)
Ich hoffe, nichts Wichtiges ausgelassen zu haben.

Re: Programm Kontrolle
Verfasst: Donnerstag 14. Juni 2012, 18:38
von Nobuddy
Leonidas, habe hier
http://code.google.com/p/psutil/ was gefunden, das recht brauchbar scheint.
psutil, ist wirklich ein sehr gutes und vielseitiges Werkzeug, Danke für den Tipp!
Ich habe mal prog_check neu geschrieben und habe da psutil verwendet, wo es mir klar war.
Poste jetzt einfach mal das Neue, in der Hoffnung nicht ganz so viel Prügel, wie das letzte Mal zu kassieren.
prog_check:
Code: Alles auswählen
#!/usr/bin/python
# -*- coding: utf-8 -*-
### Import Module
import subprocess, re, psutil, sys
from gui_dialog import dialog_result, dialog_func, showinfo_func
# Programmaufruf
def prog_check(programm, titel, pid_start):
'''
Kontrollfunktion für Start von Programmen.
Funktionsübersicht:
- Übergabe der aktuellen PID des Auftrags-Modul
- Überprüfung, ob das Auftrags-Modul evtl. schon läuft
- Freigabe des Auftrags-Modul, wenn kein Doppelstart vorliegt
- Auswahldialog bei Doppelstart
-- Auswahlmöglichkeit Abbruch (Nein),
schon gestartetes Auftrags-Modul läuft weiter
-- Auswahlmöglichkeit Beenden (Ja),
schon gestartetes Auftrags-Modul wird beendet
Bei schon gestartetem Programm, wird ein nochmaliges starten
des gleichen Programmes verhindert. Gleichzeitig wird ein
Dialog zum Beenden des aktiven Programmes angeboten.
:param programm: string, Programmnamen
:param titel: string, Programm-Bezeichnung
:param pid_current: int, aktuelle PID des gestarteten Programmes
'''
auftraginfo = 'Programm für %s starten?' % titel
process = subprocess.Popen('ps U $USER', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
process.wait()
prog_short = '{}'.format(programm[:15])
pid_current = 0
for line in process.stdout.readlines():
if prog_short in line:
if ' ' in line:
x = re.sub('^[ ]', '', line)
pid = x.partition(' ')[0]
if int(pid) != int(pid_start):
pid_current = pid
break
if pid_current == 0:
# Dialog-Fenster
# Start oder Abbruch
dialog_result.clear()
dialog_func(titel, auftraginfo)
if False in dialog_result:
info = 'Der Programmstart für %s wurde abgebrochen!' % titel
showinfo_func(titel, info)
sys.exit(1)
else:
# Dialog-Fenster
# Aktives Programm beenden
# oder Abbruch für neu gestartetes Programm
info = '''Programm läuft schon mit PID: %s
Möchten Sie dieses Programm beenden?''' % pid_current
dialog_result.clear()
dialog_func(titel, info)
if True in dialog_result:
p1 = psutil.Process(int(pid_current))
p2 = psutil.Process(int(pid_start))
p1.terminate()
p2.terminate()
sys.exit(1)
else:
p2 = psutil.Process(int(pid_start))
p2.terminate()
sys.exit(1)
Und in dem zu starteten Modul bestellorder_lieferant, sieht der betreffende Bereich, so aus:
Code: Alles auswählen
from prog_check import prog_check
PROGRAMM = 'bestellorder_lieferant_test.py'
TITEL = 'Lieferanten-Bestellungen'
def start_bestellorder():
'''
Einstiegs- und Kontrollfunktion der Auftragsabwicklung.
Ziel dieser Funktion, ist das kontrollierte Starten des Moduls
"bestellorder_lieferant.py". Die Kontrollfunktion wird durch das
Modul "prog_check.py" gesteuert.
Folgende Parameter werden dazu benötigt:
:param PROGRAMM: string, Programmnamen
:param TITEL: string, Programm-Bezeichnung
:param pid_current: int, aktuelle PID des gestarteten Programmes
'''
# PID für dieses gestartete Modul
pid_start = os.getpid()
# Kontrollmodul in der startphase
prog_check(PROGRAMM, TITEL, pid_start)
start_bestellorder()
Leider habe ich für 'pid_start = os.getpid()' noch keine Lösung mit psutil gefunden.
Kritik und Input erwünscht, am Liebsten mehr Input ...

Re: Programm Kontrolle
Verfasst: Donnerstag 14. Juni 2012, 20:53
von BlackJack
@Nobuddy: Du hast immer noch den externen ``ps``-Aufruf im Programm. Und der ist auch noch fehlerhaft implementiert, weil so wie es da gemacht wird, die Gefahr besteht, dass das Programm hängenbleibt wenn der Puffer zwischen dem ``ps`` und Deinem Programm voll ist. Dann wartet ``ps`` das Du Daten abnimmst, damit es weitere Schreiben und irgendwann zum Ende kommen kann, und Dein Programm wartet mit `wait()` darauf das ``ps`` zum Ende kommt. Und so warten beide dann bis zum St. Nummerleinstag aufeinander.
Du solltest Dir mal die Methoden auf Zeichenketten anschauen und diese beiden Zeilen durch einfachere und IMHO offensichtlichere Aufrufe ersetzen:
Code: Alles auswählen
x = re.sub('^[ ]', '', line)
pid = x.partition(' ')[0]
Letztlich ist der externe Aufruf und das Zerlegen der Zeilen aber mit dem `psutil`-Modul überflüssig.
Ich glaube ich hatte auch schon mal in einem anderen Thema von Dir ausführlich erklärt wie man gemeinsamen Code aus verschiedenen Zweigen heraus zieht. Das letzte ``else`` bei `prog_check()` ist überflüssig.
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 09:21
von Nobuddy
Hallo BlackJack, Danke für Deine Rückinfo.
Zuerst zu dem externen ``ps``-Aufruf im Programm.
Da habe ich noch keine Alternative gefunden. Bei psutil wird ja immer eine PID vorausgesetzt, was ich in dem Moment noch nicht habe. Ich habe ja nur die aktuelle PID des jetzt gerade gestarteten Programmes, aber ob es noch weitere PIDś zu diesem Programm gibt z.B. Doppelstart, kann ich nur so herausfinden. 'ps U $USER' ist dafür der richtige Befehl. Vielleicht gibt es da auch eine Möglichkeit mit psutil, dies ist mir im Moment aber noch nicht bekannt.
Du hast auch einen Fehler angesprochen (fehlerhaft implementiert), bin mir da jetzt nicht ganz sicher, ob ich dies durch diese Änderung behoben habe.
Code: Alles auswählen
process = subprocess.Popen('ps U $USER', shell=True, stdout=subprocess.PIPE)
process.wait()
'process.wait()' setze ich ein, da ich gemerkt habe, daß ohne dies zu einem Fehler führt, der dann keinen Wert liefert und somit das Programm nicht funktioniert.
Meine Kenntnisse sind da wie Du weißt, nicht die Besten. Bitte korrigiere mich, wenn ich da falsch liege!
Das mit 'wenn der Puffer zwischen dem ``ps`` und Deinem Programm voll ist', muß ich passen. Wie groß ist so ein Puffer und von was hängt dieser ab?
Bei der Zeichenkette:
Code: Alles auswählen
x = re.sub('^[ ]', '', line)
pid = x.partition(' ')[0]
habe ich dies so geändert:
Kommt dies Deiner Anmerkung nach?
Das Zerlegen der Zeilen, habe ich so geändert:
Code: Alles auswählen
psutil.Process(int(pid_current)).terminate()
psutil.Process(int(pid_start)).terminate()
Die letzte ``else`` bei `prog_check()` ist nicht überflüssig, habe es getestet.
Bei einem Doppelstart des Programms, werde ich ja dann über einen Dialog gefragt, ob die bereits laufende PID des zuerst gestarteten Programms, beendet werden soll.
Bei Ja, werden beide PIDś (die bereits laufende PID und die neu gestartete PID) beendet.
Bei Nein, wird die neu gestartete PID beendet und die bereits laufende PID bleibt weiter aktiv.
Lasse ich nun den else_Zweig weg, so läuft auch die neu gestartete PID weiter. Das habe ich zumindest bei mir festgestellt.
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 10:12
von BlackJack
@Nobuddy: Wenn Du noch keine Alternative zum ``ps``-Aufruf im `psutil`-Modul gefunden hast, dann kannst Du nicht gesucht haben. In der Dokumentation stehen nur drei Funktionen zum Thema Prozesse und nur mit einer von den dreien lassen sich die laufenden Prozesse *nicht* ermitteln.
Dadurch das Du `stderr` nicht umleitest, behebst Du das Problem nicht. Wie gross so ein Puffer ist hängt vom Betriebssystem und dessen Einstellungen ab. Wobei das keine Einstellungen sein müssen an die man heran kommt — das kann auch beim Übersetzen des Betriebssystemkerns festgelegt worden sein. Mit anderen Worten: Du hast da keinen Einfluss drauf, solltest also immer davon ausgehen, dass der Puffer zu klein ist um alle Daten aufnehmen zu können.
`re.sub()` und `partition()` in eine Zeile zu schreiben macht es nicht einfacher weil es ja immer noch die gleichen Funktionen/Methoden sind. Das `re.sub()` lässt sich durch eine Methode auf Zeichenketten aufrufen, und `partition()` habe ich ehrlich gesagt IIRC noch nie verwendet. Das ist nicht die erste Wahl wenn man eine Zeichenkette an einem bestimmten Zeichen trennen möchte.
Natürlich kann man das ``else`` nicht einfach ersatzlos streichen. Man kann das ganze ``if``/``else``-Konstrukt aber so umschreiben, dass es kürzer ist und ohne ``else`` auskommt. Eben in dem man den gemeinsamen Code aus beiden Zweigen heraus zieht.
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 11:47
von Nobuddy
@BlackJack: Ich werde mir nochmals die Dokumentation genau durchnehmen, Danke für die Info!
Danke auch für Deine Erklärung zum Puffer. Mir ist zwar noch nicht klar, wie ich dies beheben bzw. ausschließen kann, aber vielleicht komme ich noch drauf oder Du zeigst mir eine Alternative zu meinem Code.
Wie ich `re.sub()` durch eine Methode auf Zeichenketten aufrufen kann, da fehlt mir noch die Programmiererfahrung.
Statt `partition()` könnte ich auch `split()` nehmen, wäre das eine bessere Alternative?
Das mit dem ``if``/``else``-Konstrukt, werde ich mir nochmals durch den Kopf gehen lassen, vielleicht komme ich auf die Lösung, die Du meinst.
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 11:59
von BlackJack
@Nobuddy: `re.sub()` kann man nicht auf Zeichenketten aufrufen. Es gibt dort aber eine Methode die genau das macht was Du mit `re.sub()` erreichst. Auf ein so einfaches Problem muss man nicht gleich reguläre Ausdrücke loslassen.
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 15:03
von Nobuddy
@BlackJack, Danke für Dein Nachhaken, jetzt bin ich selbst drauf gekommen!
Code: Alles auswählen
for line in process.stdout.readlines():
if prog_short in line:
pid = line.strip()[:6]
if int(pid) != int(pid_start):
pid_current = pid
break
Das sieht schon besser aus, oder?
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 15:05
von BlackJack
@Nobuddy: Ja das sieht besser aus. Jetzt musst Du es nur noch komplett weglassen und stattdessen `psutil` verwenden.

Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 17:02
von Nobuddy
@BlackJack, da bin ich dabei, habe aber momentan noch Tomaten auf den Augen und suche immer noch die zwei, bei denen sich die Prozesse ermitteln lassen.
Nachtrag
PS: Ich glaub, jetzt bin ich auf der richtigen Spur ... wartet noch ein wenig und lasst Euch überraschen!
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 17:04
von Hyperion
Nobuddy hat geschrieben:
Jetzt habe ich auch Deinen berechtigten Einwand verstanden, daß eine Funktion nur für einen DOC-String nichts bringt.
Mir ging es primär vor allem darum, dass Du Dich selber mehr hinterfragen musst, *was* Du beim Implementieren schreibst. Du musst selber Deinen Code immer und immer wieder hinterfragen, bis Du verstehst und verinnerlicht hast, was Du da formulierst! Und bei den "doppelten" ``defs`` hast Du imho eben nicht nachgedacht.
Gleiches galt für die anderen Fälle, die ich ansprach

Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 17:46
von Nobuddy
@Hyperion, ja da hast Du völlig Recht, das mit dem 'immer wieder hinterfragen', da muß ich noch mehr dran arbeiten.
@BlackJack, ich bin jetzt auf diese Lösung mit psutil gekommen:
Code: Alles auswählen
prog_short = '{}'.format(programm[:15])
pid_current = 0
for pid in psutil.get_pid_list():
if prog_short in psutil.Process(pid).name:
if int(pid) != int(pid_start):
pid_current = pid
break
Alles andere ist verschwunden.

Mit diesem Konstrukt habe ich bemerkt, daß es manchmal hakt.
Rufe ich das Programm von verschiedenen Stellen aus mehrfach auf (Konsole, über das K-Menü und Geany), dann ist nicht immer sichergestellt, daß die richtige Meldung kommt.
In meinem alten Konstrukt, konnte ich das noch nicht feststellen.
Vielleicht hast Du mir da einen Tipp, was noch fehlt oder ist vielleicht der Aufbau des Konstruktes falsch?
Im anderen Programm, von wo aus ich prog_check aufrufe, habe ich noch diese Zeile:
Mit pid_start übergebe ich ja dann die aktuelle PID des gestarteten Programmes an prog_check zum Verarbeiten weiter. Da habe ich noch keine Alternative von psutil gefunden.
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 17:51
von Hyperion
Darüber hast Du aber wieder nicht nachgedacht

Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 18:00
von BlackJack
@Nobuddy: Jetzt musst Du nur noch die andere Funktion nehmen, wo in der Dokumentation extra erwähnt wird, dass sie für diesen Fall vorzuziehen ist, weil es keine „race condition” gibt.
Das beschränken auf 15 Zeichen kannst Du Dir übrigens auch ganz sparen.
Re: Programm Kontrolle
Verfasst: Freitag 15. Juni 2012, 18:07
von Nobuddy
Habe gerade meinen obigen Post editiert.
Hätte warten sollen ..., mal schauen ob ich das Andere auch noch heraus finde.
Das mit den 15 Zeichen-Beschränkung, war zur Sicherheit.
Aber wenn es auch ohne geht, lasse ich dies dann gerne weg.

Re: Programm Kontrolle
Verfasst: Samstag 16. Juni 2012, 11:48
von Nobuddy
So Leute, das war ein heißer Kampf ..., aber er hat sich gelohnt!
Hier meine Lösung:
Code: Alles auswählen
for pid in psutil.process_iter():
if (programm in psutil.Process(int(pid.pid)).cmdline) or (prog_short in psutil.Process(int(pid.pid)).name):
if int(pid.pid) != int(pid_start):
pid_current = pid.pid
break
Ohne die Verwendung von prog_short, funktioniert es nicht einwandfrei.
Eine wirkliche Begründung, fehlt mir dafür.
prog_check (Programmkontrolle) sieht nun so aus:
Code: Alles auswählen
#!/usr/bin/python
# -*- coding: utf-8 -*-
### Import Module
import subprocess, re, psutil, sys, os
from gui_dialog import dialog_result, dialog_func, showinfo_func
# Programmaufruf
def prog_check(programm, titel):
'''
Kontrollfunktion für Start von Programmen.
Funktionsübersicht:
- Übergabe der aktuellen PID des Auftrags-Modul
- Überprüfung, ob das Auftrags-Modul evtl. schon läuft
- Freigabe des Auftrags-Modul, wenn kein Doppelstart vorliegt
- Auswahldialog bei Doppelstart
-- Auswahlmöglichkeit Abbruch (Nein),
schon gestartetes Auftrags-Modul läuft weiter
-- Auswahlmöglichkeit Beenden (Ja),
schon gestartetes Auftrags-Modul wird beendet
Bei schon gestartetem Programm, wird ein nochmaliges starten
des gleichen Programmes verhindert. Gleichzeitig wird ein
Dialog zum Beenden des aktiven Programmes angeboten.
:param programm: string, Programmnamen
:param titel: string, Programm-Bezeichnung
'''
auftraginfo = 'Programm für %s starten?' % titel
pid_start = psutil.Process(os.getpid()).pid
prog_short = '{}'.format(programm[:15])
pid_current = 0
for pid in psutil.process_iter():
if (programm in psutil.Process(int(pid.pid)).cmdline) or (prog_short in psutil.Process(int(pid.pid)).name):
if int(pid.pid) != int(pid_start):
pid_current = pid.pid
break
if pid_current == 0:
# Dialog-Fenster
# Start oder Abbruch
dialog_result.clear()
dialog_func(titel, auftraginfo)
if False in dialog_result:
info = 'Der Programmstart für %s wurde abgebrochen!' % titel
showinfo_func(titel, info)
sys.exit(1)
else:
# Dialog-Fenster
# Aktives Programm beenden
# oder Abbruch für neu gestartetes Programm
info = '''Programm läuft schon mit PID: %s
Möchten Sie dieses Programm beenden?''' % pid_current
dialog_result.clear()
dialog_func(titel, info)
if True in dialog_result:
psutil.Process(int(pid_current)).terminate()
psutil.Process(int(pid_start)).terminate()
sys.exit(1)
else:
psutil.Process(int(pid_start)).terminate()
sys.exit(1)
if __name__ == "__main__":
prog_check(programm, titel)
In dem zu startetenden Programm, ist dann noch folgende Einträge nötig, damit prog_check seinen Dienst tun kann.
Code: Alles auswählen
from prog_check import prog_check
PROGRAMM = 'meinprogramm.py'
TITEL = 'Bezeichnung für Programm'
def start_bestellorder():
'''
Einstiegs- und Kontrollfunktion der Auftragsabwicklung.
Ziel dieser Funktion, ist das kontrollierte Starten des Moduls
"bestellorder_lieferant.py". Die Kontrollfunktion wird durch das
Modul "prog_check.py" gesteuert.
Folgende Parameter werden dazu benötigt:
:param PROGRAMM: string, Programmnamen
:param TITEL: string, Programm-Bezeichnung
'''
prog_check(PROGRAMM, TITEL)
start_bestellorder()
Das schöne aber daran, es funktioniert in allen Lagen.

Re: Programm Kontrolle
Verfasst: Samstag 16. Juni 2012, 15:34
von BlackJack
@Nobuddy: Es ist schon wieder super kompliziert und unnötig umständlich. Schau doch mal was `process_iter()` liefert. Der Name der Funktion verrät es doch im Grunde genommen schon.
Re: Programm Kontrolle
Verfasst: Montag 18. Juni 2012, 08:24
von Nobuddy
@BlackJack, Danke nochmals für Deine Kritik, ohne die wäre ich nicht auf diese so einfache Lösung gekommen!
Ich muß den auf 15 Stellen gekürzten Programmanmen verwenden, da bei 'print prog.name' nur die ersten 15 Stellen des Programmnamens ausgegeben werden und es sonst zu keinem positiven Ergebnis kommt.
Code: Alles auswählen
prog_short = '{}'.format(programm[:15])
pid_current = 0
for prog in psutil.process_iter():
if prog.name == prog_short and int(prog.pid) != int(pid_start):
pid_current = prog.pid
break