Hier eine Möglichkeit, wie man mit einem Lockfile sicherstellen kann, dass die eigene Anwendung nur einmal gestartet werden kann.
Es wird ein Lockfile angelegt und beim Beenden des Programms wieder gelöscht. Damit ein nicht gelöschtes Lockfile den Start der Anwendung nicht blockiert, wird in das Lockfile die PID des Programmes geschrieben. Diese wird beim Starten des Programms ausgelesen und es wird geprüft, ob ein Programm mit der im Lockfile angegebenen PID noch läuft.
Läuft das Programm mit der angegebenen PID *nicht*, dann wird das Lockfile gelöscht und die Anwendung wird gestartet.
Alls Fallback wurde eine XMLRPC-Lösung mit eingebaut. Können die laufenden PIDs nicht ermittelt werden, dann startet sich ein XMLRPC-Server, der die Anwendung als "gesperrt" kennzeichnet.
first_start.py:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
"""
Die Klasse ``FirstStart`` ist dazu da, um zu prüfen ob ein Programm bereits
gestartet wurde. Der Mechanismus arbeitet mit einem Lockfile. Können die
PIDs der laufenden Programme nicht eruiert werden, dann wird auf eine Lösung
mit XMLRPC ausgewichen. Einen Beispielaufruf findet man in der ``main()``.
Created: 2006-12-13 by Gerold Penz - gerold.penz(at)tirol.utanet.at
Requirements: Python: http://www.python.org/
"""
import os
import sys
def _get_current_pids():
"""
Returns current process-id's.
:return: List with process-id's.
"""
if sys.platform.startswith("win"):
# Windows
try:
import win32process
# pywin32 is installed --> EnumProcesses
return list(win32process.EnumProcesses())
except ImportError:
try:
import ctypes as ct
# ctypes is installed --> try psapi.dll
psapi = ct.windll.psapi
arr = ct.c_long * 1024
process_ids = arr()
cb = ct.sizeof(process_ids)
bytes_returned = ct.c_ulong()
psapi.EnumProcesses(ct.byref(process_ids), cb, ct.byref(bytes_returned))
return sorted(list(set(process_ids)))
except ImportError:
# pywin32 and ctypes are not installed --> tasklist.exe
# runs with Windows XP and higher.
import csv
csvlines = []
current_pids = []
for line in os.popen("tasklist.exe /fo csv /nh"):
line = line.strip()
if line:
csvlines.append(line)
for line in csv.reader(csvlines):
current_pids.append(int(line[1]))
if not current_pids:
raise NotImplementedError("tasklist.exe not found (>WinXP)")
return current_pids
else:
# Linux, Cygwin
current_pids = []
for filename in os.listdir("/proc"):
if os.path.isdir(os.path.join("/proc", filename)):
try:
current_pids.append(int(filename))
except ValueError:
pass
return current_pids
class FirstStart(object):
usexmlrpc = False
def __init__(self, appname):
"""
Initialisiert die Klasse und legt den Lockfilenamen fest.
:param appname: Eindeutiger Name der Anwendung. Anhand dieses
Parameters wird der Name des Lockfiles oder der Serverport für den
XMLRPC-Server generiert.
"""
self.appname = appname
self.xmlrpcport = None
# Lockfilename festlegen
appdatadir = os.environ.get("APPDATA", None)
if appdatadir:
lockdir = os.path.join(appdatadir, "Lockfiles")
else:
lockdir = "/var/lock"
self.lockfiledir = lockdir
self.lockfilename = os.path.join(lockdir, appname + "_pylock.lock")
def _get_lockfile_pid(self):
"""
Gibt die PID zurück, die im Lockfile steht.
"""
f = file(self.lockfilename, "r")
try:
pid = int(f.readline().strip())
except ValueError:
import warnings
warnings.warn("Lockfile without valid PID: '%s'" % self.lockfilename)
raise
f.close()
return pid
def _exists_lockfile(self):
"""
Prüft ob das Lockfile existiert.
"""
return os.path.isfile(self.lockfilename)
def __xmlrpc_server(self, port):
"""
XMLRPC-Server. Dieser läuft, gestartet von ``self._start_xmlrpc_server``,
innerhalb eines eigenen Threads.
"""
from SimpleXMLRPCServer import SimpleXMLRPCServer
def is_up():
return True
server = SimpleXMLRPCServer(("localhost", port))
server.register_function(is_up)
server.serve_forever()
def _start_xmlrpc_server(self, port):
"""
Startet den XMLRPC-Server
"""
from thread import start_new_thread
start_new_thread(self.__xmlrpc_server, (port,))
def create_lock(self):
"""
Erstellt ein Lockfile mit der aktuellen PID oder startet den
XMLRPC-Server.
"""
if not self.usexmlrpc:
lockfiledir = self.lockfiledir
lockfilename = self.lockfilename
if not(os.path.isdir(lockfiledir)):
os.makedirs(lockfiledir)
f = file(lockfilename, "w")
f.write(str(os.getpid()))
f.close()
else:
self._start_xmlrpc_server(self.xmlrpcport)
create_lockfile = create_lock # --> um abwärtskompatibel zu bleiben
def delete_lock(self):
"""
Löscht das Lockfile der Anwendung. Der XMLRPC-Server wird NICHT beendet, da
dieser sowiso nach Programmende automatisch beendet wird.
(Sollte es notwendig sein, dass der XMLRPC-Server mit dem Aufruf dieser
Methode beendet wird, dann müsste man den XMLRPC-Server so umschreiben,
dass nicht ``server.serve_forever()`` verwendet wird.)
"""
if not self.usexmlrpc:
if os.path.isfile(self.lockfilename):
os.remove(self.lockfilename)
else:
pass
delete_lockfile = delete_lock # --> um abwärtskompatibel zu bleiben
def is_first_start(self):
"""
Prüft ob die beim Initialisieren angegebene Anwendung bereits ausgeführt wird.
:return: True, wenn die Anwendung bereits läuft.
False, wenn die Anwendung noch nicht läuft.
"""
if not self.usexmlrpc:
# LOCKFILE-Lösung
lockfilename = self.lockfilename
# Existiert das Lockfile?
if not self._exists_lockfile():
return True
# Ist das Programm noch gestartet?
try:
lockfilepid = self._get_lockfile_pid()
currentpids = _get_current_pids()
except (NotImplementedError, ValueError, IOError):
# Fallback --> XMLRPC
self.usexmlrpc = True
return self.is_first_start()
if lockfilepid in currentpids:
# Das Programm läuft noch
return False
else:
# Das Programm mit der ermittelten PID ist nicht gestartet.
self.delete_lockfile()
return True
# OK
return True
else:
# XMLRPC-Lösung
import xmlrpclib
import socket
self.xmlrpcport = int(
"5" + str(hash(self.appname)).replace("0", "").replace("-", "")[:4]
)
try:
server = xmlrpclib.ServerProxy("http://localhost:" + str(self.xmlrpcport))
return not bool(server.is_up())
except socket.error:
return True
def main():
"""Testen"""
import time
firststart = FirstStart("testapplikation")
if firststart.is_first_start():
firststart.create_lock()
# Hier wird gearbeitet
for i in range(4):
print i,
time.sleep(1)
print
## wxPython-App
#import wx
#app = wx.PySimpleApp(True)
#print "Ich bin ein Fenster"
#app.MainLoop()
firststart.delete_lock()
else:
print "Das Programm wurde bereits gestartet."
if __name__ == "__main__":
main()
Und hier wird aufgezeigt, wie das Modul "first_start.py" in einem anderen Python-Modul verwendet werden kann:
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_lock()
###################################################
### Hier wird gearbeitet. ###
### ###
### Es ist egal, ob das Programm unter ###
### Linux oder Windows läuft. ###
### ###
### http://www.python-forum.de/topic-8282.html ###
### ###
###################################################
fs.delete_lock()
else:
print "Das Programm wurde bereits gestartet."
if __name__ == "__main__":
main()
Gerold
Edit: XMLRPC-Server als Fallback mit eingebaut.