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
modul fcntl.

http://docs.python.org/library/fcntl.html

Das ist Standard OS Kram.

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 ... :wink:

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.