Seite 2 von 7
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 07:28
von Leonidas
Nobuddy hat geschrieben:Ich suche nach einer Möglichkeit, mit der ich feststellen kann ob das Paket 'bestellorder_lieferant.py' schon läuft, um ein doppeltes Sarten dieses Paketes zu vermeiden.
Die sauberste Lösung, die vielleicht leicht zu Overkill neigt ist es, LibUnique oder direkt GTK3's Funktionalität (Unique ist in GTK aufgegangen, was mich nur mäßig begeistert) zu nutzen. Oder, wenn man so große Dependencies vermeiden will dann das Programm auf dem D-Bus SessionBus lauschen lassen. Wenn das Programm startet, dann gucken ob eine andere Instanz ggf. auf dem SessionBus bereits läuscht und fertig.
Lösungen mit Lockfiles haben, für User-Programme oftmals das Problem, dass wenn ein User das startet, kein anderer User das Programm nutzen kann, weil es global auf dem ganzen Rechner sperrt, was nicht sein müsste. Und gerade der SessionBus ist da eine relativ schicke Lösung, weil eben jeder User seinen eigenen hat und darüber auch Parameter übertragen werden können.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 07:59
von snafu
@Leonidas: Wäre da ein Lockfile in `~/.mein_programm/` nicht ebenso eine Möglichkeit? Dann hätte man es doch auch userbezogen, oder nicht?
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 10:43
von Hyperion
Auch wenn ich die DBus-Ide als schick empfinde, könnte mir vielleicht jemand erklären, wie man einen "Lock" auf einer Datei implementiert? (Und natürlich auch abfragt) Ich habe da im Moment keine Idee - oder gibt es da einfach Funktionen?
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 10:46
von deets
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 10:51
von Hyperion
Alles klar, danke

Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 12:39
von Nobuddy
Hyperion, ich bin mir nicht sicher, wie ich Dein Python-Pseudocode einbringe.
Ist wäre dieser Ansatz richtig?
Code: Alles auswählen
if __name__ == "__main__":
if exist(pidpath):
print "Programm läuft schon mit PID:", pidpath.read()
sys.exit(1)
else:
pidpath.write(get_pid)
start_bestellorder(TITEL, AUFTRAGINFO)
remove(pidpath)
Hier erhalte ich dann die meldung:
Code: Alles auswählen
Traceback (most recent call last):
File "start_test.py", line 56, in <module>
if exist(pidpath):
NameError: name 'exist' is not defined
Ich habe Eure weitere Diskusion verfolgt.
Gibt es da evtl. eine neuer Ansatz, wenn ja wie würde dieser aussehen?
Grüße Nobuddy
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 13:09
von Hyperion
Öh... Du musst mal lernen, mehr nachzudenken! Du hast ja meinen *Pseudo*Code quasi eins zu eins übernommen - das Wort "Pseudo" legte doch nahe, dass es sich nur um eine simple Veranschaulichung des Ablaufs handeln sollte und nicht um *lauffähigen* Code. Dann hätte ich eher das Wort Code-Template gewählt.
- Mit `exist(pidpath)` wollte ich nur ausdrücken, dass Du eine Funktionalität benötigst, welche prüft, ob die PID-Datei existiert. Eine Funktion `exist` gibt es natürlich gar nicht!
- `pidpath.write` ist natürlich auch nicht funkionsfähig - es sollte nur verdeutlichen, dass man diese Datei anlegen muss, wenn sie noch nicht existiert. Zugegeben sehe ich jetzt, dass diese Pseudocodedarstellung schlecht war. Ein `write(pidpath)` wäre da klarer gewesen.
- `start_bestellorder()` wiederum hast Du an der falschen Stell eingefügt! Rein funktional macht es zwar keinen Unterschied, aber ich würde es aus dem `else`-Zwei dennoch herausziehen, da es mit der initialen Kontrolle des PID-Files nichts mehr zu tun hat. (Also die Einrückungsebene beachten!)
- `remove(pidpath)` gibt es natürlich - analog zu den obigen Themen - auch nicht.
Ich hätte Dir anstelle des Codes auch eine Art Sequenzdiagramm malen können, so wie das folgende:
Code: Alles auswählen
+--------+
| Start |
+----+---+
|
|
v
+-----------+ +------------+ +-------------+
| Prüfe, ob | False | PID-File | | Eigentliche |
| PID-File +-------->| anlegen +-------->| Funktion |
| existiert | +------------+ | ausführen |
+-----+-----+ +-----+-------+
| |
|True |
v v
+------------+ +------------+
| Programm | | PID-File |
| abbrechen | | löschen |
+------------+ +-----+------+
|
v
+-------------+
| Programm |
| beenden |
+-------------+
Aber das war mir zu aufwendig
deets wies ja schon auf die Problematik hin, dass man die Datei vor allem "locken" muss, um das Problem zu lösen, welches auftritt, wenn beim Ausführen des Programmes ein Fehler auftritt und demzufolge die Datei am Schluss nicht mehr gelöscht wird.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 13:11
von deets
Wir haben ein Paket, welches das alles ein bisschen abstrahiert & einfacher macht - kann man via pip/easy_install installieren. Doku ist leider nicht wirklich existent, aber testfaelle:
http://hg.ableton.com/abl.util/src/07b1 ... ockfile.py
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 13:22
von Nobuddy
Hyperion, ja mehr nachdenken ...
Nein, ich habe mir schon so etwas in der Art überlegt gehabt, war aber zu unsicher.
Deine Beschreibung hat mir weiter geholfen und ich denke, daß ich da etwas hin bekomme.
deets, das Paket werde ich mir mal genauer nachschauen, wenn ich so nicht weiter kommen sollte
Ist aber auch sehr umfangreich.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 15:13
von deets
Ich denke mal du wirst grosse Schwierigkeiten haben, das locking proper hinzubekommen. Das zumindest vermute ich nach deinen bisherigen Versuchen. Insofern solltest du dir das wirklich ueberlegen, abl.util zu benutzen - damit ist das ganze ein simpler aufruf + eventuell ne Exception fangen.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 15:37
von Nobuddy
Ich habe da jetzt mal eine Lösung:
Code: Alles auswählen
class DialogFenster(tkSimpleDialog.Dialog):
def foo():
'''
Das Dialog-Fenster besteht aus den Dialogen "ok" und "cancel".
Die weitere Beschreibung über die Rolle und Funktionalität,
muß noch erarbeitet werden.
'''
def body(self, master):
self.title(TITEL)
self.namen = Label(master, text=AUFTRAGINFO)
self.namen.pack(side=LEFT)
def apply(self):
self.result = True
return
def start_bestellorder(title, auftraginfo):
def foo():
'''
Haupt- und Einstiegsfunktion der Auftragsabwicklung. Sie startet einen
Dialog, in dem das Paket "bestellorder_lieferant" gestartet werden kann.
:param TITEL: string mit dem Titel des Dialogfensters
:param AUFTRAGINFO: string mit Informationen zum Auftrag
'''
NamenDialog = DialogFenster(Tk())
if NamenDialog.result == True:
try:
exist = os.getpid()
fobj = open(pidpath, 'w')
fobj.write(str(exist))
fobj.close()
from bestellorder_lieferant import auftragkontrolle
auftragkontrolle()
except ImportError:
info = 'Es gab einen Fehler, das Programm für %s konnte nicht gestartet werden!' % title
else:
info = 'Der Programmstart für %s wurde abgebrochen!' % title
tkMessageBox.showinfo('Info', info)
Tk().destroy()
if __name__ == "__main__":
exist = os.listdir(os.path.dirname(pidpath))
try:
if exist != None and os.path.getsize(os.path.join(os.path.dirname(pidpath), os.path.basename(pidpath))) > 0:
fobj = open(pidpath, "r")
for line in fobj:
wert = line
fobj.close()
print "Programm läuft schon mit PID:", wert
sys.exit(1)
except OSError:
start_bestellorder(TITEL, AUFTRAGINFO)
os.remove(pidpath)
Das einzigste Problem was besteht, ist 'os.remove(pidpath)'.
Egal wo ich dies positioniert habe, nach dem Beenden wird die Datei '/tmp/pidpath' nicht gelöscht und bei erneutem Start, wird dann natürlich angezeigt, daß das Programm schon läuft.
Evtl. Vorschläge?
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 15:44
von deets
Das funktioniert aus einer Vielzahl von Gruenden nicht. Dein akutes Problem ist natuerlich, das sys.exit nunmal das Programm beendet - wenig ueberraschend, oder?
Letztlich ist das aber irrelevant, denn wenn dein Programm abstuerzt, wird eh nicht aufgeraeumt.
Darum ist das, was du da machst, nicht genug. Warum haben wir hier wohl ueber lockfiles geredet? Das Locking ist der Weg, weil das Betriebssystem automatisch ein File un-locked, wenn der dazugehoerige Prozess aus welchen Gruenden auch immer stirbt.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 16:00
von Nobuddy
Ich habe extra den Ordner /tmp für die Datei pidpath gewählt, da wird zumindest beim Neustart aufgeräumt.
Aber klar eine saubere Lösung ist das nicht.
deets, ich verstehe schon was Du mir da nahe bringen willst, aber da reichen meine Python-Kenntnisse noch nicht aus.
Außer Ihr bringt mir das Häppchenweise bei.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 16:06
von deets
Der Punkt ist: das was du machst, funktioniert nicht. Wenn du mehr nicht kannst - das ist natuerlich pech. Aber dann brauchst du das, was du da jetzt probiert hast, auch nicht machen - weil's nunmal nix hilft.
Und haeppchenweise beibringen bedeutet augenscheinlich "fuer dich machen". Danke, aber ich hab' schon nen Job.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 16:48
von Nobuddy
deets, nur keine Angst, das möchte ich auf keinen Fall, daß die Arbeit Andere für mich machen.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 17:05
von deets
Na denn - hier ist eine Anwendung von abl.util.Lockfile, wie du sie brauchst:
http://hg.ableton.com/abl.util/src/07b1 ... e.py#cl-29
Dann mach dich man an die Arbeit.
Re: Programm Kontrolle
Verfasst: Mittwoch 30. Mai 2012, 19:23
von Hyperion
Ich bin generell skeptisch, wieso man das bei einer simplen Desktop-Applikation unbedingt erreichen will. Ich würde mich ja eher auf diese i.A. konzentrieren (z.B. das Problem mit den globalen Variablen und dem Problem beim Ableiten einer Dialogklasse), anstatt so einen Nebenkriegsschauplatz auf zu machen. Oder ist das eine essenzielle Anforderung?
Re: Programm Kontrolle
Verfasst: Donnerstag 31. Mai 2012, 03:23
von Leonidas
snafu hat geschrieben:@Leonidas: Wäre da ein Lockfile in `~/.mein_programm/` nicht ebenso eine Möglichkeit? Dann hätte man es doch auch userbezogen, oder nicht?
An sich schon, aber man hat da keine Möglichkeit mit einem laufendem Programm zu interagieren. Und halt reinventing the wheel. Muss man eben wissen was die eigenen Anforderungen sind.
Re: Programm Kontrolle
Verfasst: Donnerstag 31. Mai 2012, 18:04
von Nobuddy
So Leute,
habe erfolgreich das Konstrukt umgesetzt
Wenn das Programm gestartet ist und ich dies nochmals starten will, ist ein zweiter Start nicht mehr möglich und man erhält die Info "Programm läuft schon mit PID: 4711".
Hier mal das Konstrukt:
Code: Alles auswählen
class DialogFenster(tkSimpleDialog.Dialog):
def foo():
'''
Das Dialog-Fenster besteht aus den Dialogen "ok" und "cancel".
Die weitere Beschreibung über die Rolle und Funktionalität,
muß noch erarbeitet werden.
'''
def body(self, master):
self.title(TITEL)
self.namen = Label(master, text=AUFTRAGINFO)
self.namen.pack(side=LEFT)
def apply(self):
self.result = True
return
def start_bestellorder(title, auftraginfo):
def foo():
'''
Haupt- und Einstiegsfunktion der Auftragsabwicklung. Sie startet einen
Dialog, in dem das Paket "bestellorder_lieferant" gestartet werden kann.
:param TITEL: string mit dem Titel des Dialogfensters
:param AUFTRAGINFO: string mit Informationen zum Auftrag
'''
NamenDialog = DialogFenster(Tk())
# Liste Dateien aus pidpath_path auf
exist = os.listdir(pidpath_path)
# Überprüfe pidpath in Liste
if pidpath in exist:
fobj = open('/tmp/%s' % pidpath, "r")
for line in fobj:
wert = line
fobj.close()
# Erstelle Datei über aktive IDś
os.system('ps -u > /tmp/check 2>/dev/null')
# Überprüfe ob ID aus pidpath aktuell ist
id_ok = False
try:
fobj = open('/tmp/check', "r")
for line in fobj:
if wert in line:
id_ok = True
fobj.close()
except UnboundLocalError:
pass
try:
# ID ist aktuell, Abbruch
if id_ok == True:
print "Programm läuft schon mit PID:", wert
sys.exit(1)
# ID ist nicht aktuell, pidpath wird gelöscht
if id_ok == False:
try:
try:
os.remove('/tmp/%s' % pidpath)
except OSError:
pass
if NamenDialog.result == True:
exist = os.getpid()
fobj = open('/tmp/%s' % pidpath, 'w')
fobj.write(str(exist))
fobj.close()
from bestellorder_lieferant import auftragkontrolle
auftragkontrolle()
else:
info = 'Der Programmstart für %s wurde abgebrochen!' % title
except ImportError:
info = 'Es gab einen Fehler, das Programm für %s konnte nicht gestartet werden!' % title
tkMessageBox.showinfo('Info', info)
Tk().destroy()
except OSError:
pass
if __name__ == "__main__":
start_bestellorder(TITEL, AUFTRAGINFO)
Grüße Nobuddy
Re: Programm Kontrolle
Verfasst: Donnerstag 31. Mai 2012, 19:56
von deets
Na, dann hoffen wir mal alle, dass es keine race-conditions gibt, weil jemand das Programm mit Doppelklick startet (und dadurch aus versehen gleich 2mal)
Von einigen anderen Unschoenheiten mal abgesehe: UnboundLocalError abzufangen bedeutet, dass du einen Programmierfehler gemacht hast, und den nicht behebst, sondern einfach irgendwie weiterwurschtelst.