Seite 1 von 2

Sicherstellen,dass eine wxPython-App nur 1x ausgeführt wird?

Verfasst: Sonntag 10. Dezember 2006, 00:19
von oliver1974
Hallo nochmals,

Ist es irgendwie möglich, ohne den Umweg über ein Lock-File
oder dergleichen plattformübergreifend sicherzustellen, dass eine
wxPython - Applikation nur in einer Instanz ausgeführt wird?

Verfasst: Sonntag 10. Dezember 2006, 14:32
von Leonidas
Du kannst in deiner App einen XML-RPC oder sonstigen Server starten und beim start gucken ob man sich zu einem solchen Verbinden kann. Wenn ja: Programm läuft, also sofort beenden. Wenn nein, dann starten und Server laufen lassen.
Nein, elegant ist das nicht, aber effektiv. Und wenn dein Server nur auf local horcht, gibt das nichtmal ein Sicherheitsproblem.

Verfasst: Dienstag 12. Dezember 2006, 12:52
von oliver1974
Hmm, ist natürlich ein Ansatz...

Nur was, wenn da zufälligerweise schon so ein Server läuft, es müsste ja dann schon was "einzigartiges" sein....

Verfasst: Dienstag 12. Dezember 2006, 13:59
von gerold
oliver1974 hat geschrieben:Hmm, ist natürlich ein Ansatz...
Hi oliver!

...in etwa so:

Code: Alles auswählen

# -*- coding: iso-8859-1 -*-

import wx
from thread import start_new_thread
from SimpleXMLRPCServer import SimpleXMLRPCServer
import xmlrpclib
import socket

PORT = 26214

wx.SetDefaultPyEncoding("iso-8859-1")


def is_first_client():
    """
    Gibt True zurück, wenn diese Anwendung nur einmal gestartet wurde
    """
    
    try:
        server = xmlrpclib.ServerProxy("http://localhost:" + str(PORT))
        return not bool(server.is_up())
    except socket.error:
        return True
    

def lock_app():
    """
    Startet einen XMLRPC-Server um die Anwendung für weitere Aufrufe zu sperren.
    """
    
    def is_up():
        return True
    
    server = SimpleXMLRPCServer(("localhost", PORT)) 
    server.register_function(is_up)
    server.serve_forever()


if __name__ == "__main__":
    
    app = wx.PySimpleApp()

    if is_first_client():
        # Anwendung sperren
        start_new_thread(lock_app, ())
        
        f = wx.Frame(None, -1, "Hallo")
        f.Center()
        f.Show()
        
        app.MainLoop()
    else:
        wx.MessageBox("Die Anwendung wurde bereits gestartet!")
Allerdings kann ich die Anwendung jetzt im Moment nicht unter Linux testen.

mfg
Gerold
:-)

Verfasst: Dienstag 12. Dezember 2006, 16:07
von sape
Warum so kompliziert Leute? :D

Ne Datei im userspace/MyApp. Z.B.: Ne INI.

Wenn MyApp gestartet ist (Instanz 1), setzt es den wert auf True.

Code: Alles auswählen

isrun.ini
[status]
isrun = True
Wenn jetzt MyApp nochmal gestartet (Instanz 2) wird überprüft es erstmal ob "isrun" False ist. Wenn nicht da beendet sich MyApp.

Wenn Instanz 1 Beendet wird setzt es den Wert "isrun", kurz vorm ".destroy()"-Aufruf, auf Flase.

lg

EDIT: Die einfachste Lösung ohne irgendwelche Netzwerk geschichten ;)

Verfasst: Dienstag 12. Dezember 2006, 16:24
von gerold
XtraNine hat geschrieben:Warum so kompliziert Leute? :D
Hi XtraNine!

Weil nach einer Lösung ohne Lock-File gefragt wurde. Deine Lösung ist nichts anderes als ein komplizierteres Lockfile.

Plattformübergreifende Lockfile-Lösungen haben meist den Nachteil, dass diese nach einem Fehler im Programm den Start der Anwendung verhindern. Das kann man zwar umgehen, indem man ein paar Prüfungen einbaut. Z.B. ob das Lockfile älter als der Startzeitpunkt des Computers ist. Wenn ja, dann wird das Lockfile gelöscht und die Anwendung gestartet...

XMLRPC ist plattformunabhängig und wenn der Computer neu gestartet wurde, ist alles wieder im Originalzustand.

Wenn die Lockfile-Lösung durchdacht programmiert wurde, dann würde ich diese auf jeden Fall der XMLRPC-Lösung vorziehen. Aber wie ich schon schrieben -- es wurde nach einer Lösung ohne Lockfile gefragt. :wink:

lg
Gerold
:-)

Verfasst: Dienstag 12. Dezember 2006, 16:28
von gerold
PS:

Das ideale Lockfile enthält die PID des gestarteten Programmes.

Wenn das Programm gestartet wird, wird zuerst geprüft ob es ein Lockfile gibt. Gibt es ein Lockfile, dann wird geprüft ob ein Programm mit der im Lockfile gespeicherten PID noch läuft. Wenn ja, dann wird das neu gestartete Programm beendet. Wenn nein, dann wird das alte Lockfile gelöscht und mit dem Start des neuen programms fortgesetzt.

lg
Gerold
:-)

Verfasst: Dienstag 12. Dezember 2006, 16:36
von gerold
...

Unter Windows und mit installiertem ``pywin32`` bekommt man die Liste der Prozesse so:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import os
import win32process

pid = os.getpid()
running_pids = win32process.EnumProcesses()

print "Eigener PID:", pid
print "Laufende Prozesse:", running_pids
mfg
Gerold
:-)

Verfasst: Dienstag 12. Dezember 2006, 17:03
von gerold
...

Unter Linux fällt mir nichts besseres ein, um die PIDs zu ermitteln:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import os

pid = os.getpid()

running_pids = []
for filename in os.listdir("/proc"):
    if os.path.isdir(os.path.join("/proc", filename)):
        try:
            running_pids.append(int(filename))
        except ValueError:
            pass

print "Eigene PID:", pid
print "Laufende Prozesse:", running_pids
lg
Gerold
:-)

Verfasst: Dienstag 12. Dezember 2006, 17:42
von lunar
Zum Überprüfen reicht unter Linux doch folgendes:

Code: Alles auswählen

# hier das lockfile mit pid auslesen
if pid in os.listdir('/proc'):
    sys.exit(0)
else:
    # Anwendung starten
Wozu also die komplette Prozessliste?

Verfasst: Dienstag 12. Dezember 2006, 18:19
von gerold
lunar hat geschrieben:Wozu also die komplette Prozessliste?
Hi lunar!

Gute Idee! 8)

mfg
Gerold
:-)

Verfasst: Dienstag 12. Dezember 2006, 19:54
von sape
gerold hat geschrieben:
XtraNine hat geschrieben:Warum so kompliziert Leute? :D
[...]
Weil nach einer Lösung ohne Lock-File gefragt wurde. Deine Lösung ist nichts anderes als ein komplizierteres Lockfile.
[...]
Oh, das hatte ich übersehen.
BTW: Deine Vorschläge für ein gutes Lock-File sind sehr gut 8) Werde ich mit übernehmen :)

P.S.: Leute das Forum ist erste Sahne!! 8)

Verfasst: Mittwoch 13. Dezember 2006, 10:20
von oliver1974
sape hat geschrieben: P.S.: Leute das Forum ist erste Sahne!! 8)
Dem ist eigentlich nix mehr hinzuzufügen!

Wegen dem Lock-File.. Ja, die Idee hatte ich natürlich auch schon,
nur ist es da eben das Problem (wie schon geschrieben) dass,
wenn die Anwendung sich nicht sauber beendet, der Lockfile
da stehen bleibt.. und schon ist das ganze nicht mehr so trivial.

Die Tipps gehören ja eigentlich ins Wiki, das Problem taucht ja
immer wieder mal auf..

Verfasst: Mittwoch 13. Dezember 2006, 10:35
von lunar
oliver1974 hat geschrieben:Wegen dem Lock-File.. Ja, die Idee hatte ich natürlich auch schon,
nur ist es da eben das Problem (wie schon geschrieben) dass,
wenn die Anwendung sich nicht sauber beendet, der Lockfile
da stehen bleibt.. und schon ist das ganze nicht mehr so trivial.
Wieso?
Wenn du die PID im Lockfile speicherst, kannst du doch beim Starten der Anwendung die PID auslesen. Dann kannst du überprüfen, ob ein entsprechender Prozess läuft und ob er zu deiner ausführbaren Datei passt.

Was ist daran nicht trivial? Es ist auch nicht schwerer als einen Server zu starten, es ist nur ein bisschen mehr Code ;)

Gruß
lunar

Verfasst: Mittwoch 13. Dezember 2006, 12:40
von gerold
lunar hat geschrieben:...es ist nur ein bisschen mehr Code ;)
Hi lunar!

Das kann man laut sagen! :mrgreen:

http://www.python-forum.de/topic-8282.html

lg
Gerold
:-)

Verfasst: Mittwoch 13. Dezember 2006, 18:23
von lunar
Well, das sieht aber aufwändig aus :shock:

Also bei geht das irgendwie kürzer:

Code: Alles auswählen

import os

# sowieso nur unter linux, deswegen kein os.path.join
pid_file = os.path.expanduser('~'/'.foo'/'foo.pid')

# lesen
try:
    f = open(pid_file)
    try:
        pid = f.read()
        # überprüfen
        if pid in os.listdir('/proc'):
            # beenden
            sys.exit()
    finally:
        # datei schließen
        f.close()
except IOError:
    # uups, nicht da => nix tun
    pass

# starten
f = open(pid_file, 'w')
f.write(os.getpid())
f.close()

# beenden des programmes
os.remove(pid_file)
Der Code mag fehlerhaft sein, er entsprang meinem Gehirn gerade eben und ist nicht getestet ;), aber so ähnlich könnte ein doch der Code für Lockfile aussehen, oder?

Aber da wir sowieso alle nach KLOC bezahlt werden... ;)

Gruß
lunar

Verfasst: Donnerstag 14. Dezember 2006, 09:06
von oliver1974
Hmm, warum das
"sowieso nur unter linux",
os.getpid() geht doch laut Doku auch unter Windows, oder?

"Return the current process id. Availability: Unix, Windows. "

Legen wir noch mal eine Schippe drauf, wie siehts denn mit MacOSX
aus.. ;-)

(Ich werde jetzt erstmal die Vorschläge bei mir integrieren und dann testen...)

Verfasst: Donnerstag 14. Dezember 2006, 09:32
von Rebecca
oliver1974 hat geschrieben:Hmm, warum das
"sowieso nur unter linux",
os.getpid() geht doch laut Doku auch unter Windows, oder?
Fuer Gerolds und lunars Methode benoetigt man aber nicht nur die PID des aktuellen Prozesses, sondern auch die PIDs aller laufender Prozesse. Und das geht halt mit Linux und Windows unterschiedlich.
oliver1974 hat geschrieben:Legen wir noch mal eine Schippe drauf, wie siehts denn mit MacOSX
MacOSX ist auch nur ein Unix-Derivat.

Verfasst: Donnerstag 14. Dezember 2006, 10:02
von gerold
Hi lunar!

Einer der wichtigsten Gründe, warum ich mit Python entwickle --> Plattformunabhängigkeit.
Wir alle wissen, dass man meist weniger Code braucht, um nur für eine Plattform zu programmieren. Und *ja*, deine Lösung würde nach ein paar kleinen Änderungen unter Linux einwandfrei funktionieren.

``first_start.py`` ist so geschrieben, dass es als Erweiterungsmodul eingesetzt werden kann. So hat man wiederverwendbaren Code, der jetzt schon mindestens unter Linux, Cygwin und Windows läuft. Durch den Umweg über die gemeinsame Liste der PIDs ändert sich die Logik des Hauptprogrammes nicht -- auch wenn man es unter verschiedensten Betriebssystemen laufen lässt. Dem Hauptprogramm ist die Plattform komplett egal. Es ändert sich also nichts an dessen Programmlogik.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

from first_start import FirstStart

def main():
    fs = FirstStart("testapplikation")
    if fs.is_first_start():
        fs.create_lockfile()
        ###################################################
        ### Hier wird gearbeitet.                       ###
        ###                                             ###
        ### Es ist auch egal, ob das Programm unter     ###
        ### Linux oder Windows läuft.                   ###
        ### Man sollte ab und zu darauf achten,         ###
        ### Code zu schreiben, den man wiederverwenden  ###
        ### kann.                                       ###
        ###                                             ###
        ### http://www.python-forum.de/topic-8282.html  ###
        ###                                             ###
        ###################################################
        fs.delete_lockfile()
    else:
        print "Das Programm wurde bereits gestartet."

if __name__ == "__main__":
    main()
mfg
Gerold
:-)

Verfasst: Donnerstag 14. Dezember 2006, 19:33
von lunar
gerold hat geschrieben:``first_start.py`` ist so geschrieben, dass es als Erweiterungsmodul eingesetzt werden kann. So hat man wiederverwendbaren Code, der jetzt schon mindestens unter Linux, Cygwin und Windows läuft.
Ok, für Windows kann ich zu Hause - mangels Testsystem ;) - nichts entwickeln. Wenn man das Ganze wirklich platformunabhängig gestalten will, kommt man wohl um deine Lösung nicht herum.

Was aber wiederrum die LOC für oliver1974 sehr reduziert: Für die Lockfile-Lösung kann er ja einfach auf dein Modul zurückgreifen ;)