Seite 1 von 1

exklusiver filezugriff

Verfasst: Donnerstag 9. April 2009, 12:54
von kryz
Hallo

Ich möchte eine Datei exklusiv öffnen, also so dass kein anderer Prozess dieselbe Datei öffnen kann. Mit open() scheint das nicht zu gehen und mit os.open auch nicht (oder hab ich da was übersehen?). gibt es einen (einfachen) weg, ein solches file-objekt zu erstellen?

p.s. das ganze sollte auf windows xp mit python 2.5 laufen

Verfasst: Donnerstag 9. April 2009, 13:04
von DasIch
Es geht nicht.

Verfasst: Donnerstag 9. April 2009, 13:43
von kbr
Falls diese Exklusivität nur für eine Applikation gelten soll, die parallel in mehreren Prozessen läuft, so geht es mit einem kleinen Trick: teste vor dem öffnen auf die Existenz der Datei "dateiname.lock" und unterbinde das öffnen der eigentlichen Datei "dateiname", falls die lock-Datei existiert. Andernfalls erzeuge die Datei "dateiname.lock" und öffne dann die eigentliche Datei. Beim schließen nicht vergessen, die Datei "dateiname.lock" wieder zu löschen. Unter UNIX geht das mit lock-handles eleganter, aber so funktioniert es auch bei Windows.

Verfasst: Donnerstag 9. April 2009, 14:26
von Nergal
kbr hat geschrieben:Falls diese Exklusivität nur für eine Applikation gelten soll, die parallel in mehreren Prozessen läuft, so geht es mit einem kleinen Trick: teste vor dem öffnen auf die Existenz der Datei "dateiname.lock" und unterbinde das öffnen der eigentlichen Datei "dateiname", falls die lock-Datei existiert. Andernfalls erzeuge die Datei "dateiname.lock" und öffne dann die eigentliche Datei. Beim schließen nicht vergessen, die Datei "dateiname.lock" wieder zu löschen. Unter UNIX geht das mit lock-handles eleganter, aber so funktioniert es auch bei Windows.
Das Modul msvcrt bietet etwas ähnliches.

Code: Alles auswählen

>>> import os
>>> import msvcrt
>>> FileName = "E:\\test.txt"
>>> AccessOne = open(FileName)
>>> msvcrt.locking(AccessOne.fileno(), msvcrt.LK_LOCK, os.path.getsize(FileName))
>>> AccessTwo = open(FileName)
>>> msvcrt.locking(AccessTwo.fileno(), msvcrt.LK_LOCK, os.path.getsize(FileName))
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
IOError: [Errno 36] Resource deadlock avoided
>>> 
Falls die Datei schon gelockt ist, wird ein IOError geworfen.

Die Function locking versucht glaubich 10x die Datei zu locken; nicht wundern, wenn es etwas dauert bis die Exception geworfen wird.

Verfasst: Donnerstag 9. April 2009, 14:42
von lunar
@kbr
Man darf nicht testen, ob die Lock-Datei existiert. Man muss versuchen, sie sofort atomar zu öffnen, ansonsten existiert eine Race Condition. Dazu lässt sich die Funktion "open()" auch nicht verwenden, man muss auf "os.open()" zurückgreifen.

Außerdem sollte Windows doch von Haus aus mandatory locks beim Öffnen einer Datei erzeugen, oder nicht?

Verfasst: Donnerstag 9. April 2009, 15:32
von Leonidas
Aber diese Locks gelten nicht fürs öffnen und lesen. Das geht weiterhin. Löschen kann man geöffnete Dateien zum Beispiel nicht.

Verfasst: Donnerstag 9. April 2009, 16:22
von kryz
Danke für die Antworten.

Leider funktioniert msvcrt.locking nicht für leere Dateien, ist also auch unzuverlässig.

Man könnte versuchen, die Datei umzubenennen, das geht nicht, wenn die Datei bereits geöffnet ist. Aber das ist wohl auch nicht ganz sauber.

So bleibt mir halt nur, meine eigene File Klasse auf basis von win32file zu programmieren. momentan brauche ich zum glück nur ein lock beim schreiben...

Code: Alles auswählen

import win32file

class LockedFile(object):

    def __init__(self, filePath):
        dwDesiredAccess = win32file.GENERIC_WRITE
        dwCreationDisposition = win32file.CREATE_ALWAYS        
        dwShareMode = 0  # locked für alle zugriffe
        lpSecurityAttributes = None
        dwFlagsAndAttributes = win32file.FILE_ATTRIBUTE_NORMAL
        hTemplateFile = None

        self._handle = win32file.CreateFile(filePath,
                                            dwDesiredAccess,
                                            dwShareMode,
                                            lpSecurityAttributes,
                                            dwCreationDisposition,
                                            dwFlagsAndAttributes,
                                            hTemplateFile)

    def write(self, str):
        str = str.replace('\n', '\r\n')
        win32file.WriteFile(self._handle, str)
        
    def close(self):
        self._handle.close()

Verfasst: Donnerstag 9. April 2009, 16:51
von DasIch
PEP 8 wo versteckst du dich bloß? :cry:

Verfasst: Donnerstag 9. April 2009, 17:46
von lunar
Die Schnittstelle der Klasse hält sich an PEP 8 ... die lokalen Namen in __init__() zwar nicht, nur kann man das in diesem Fall durchaus mit den Namen der Parameter rechtfertigen. So sieht die Windows-API nun mal aus, daher halte ich es sogar für sinnvoller, sich beim Umgang damit auch an ihre Konventionen zu halten, das fördert das Verständnis.

Man kann es mit PEP 8 auch übertreiben ...

Verfasst: Donnerstag 9. April 2009, 19:45
von Leonidas
``str`` zu überschreiben ist aber so oder so keine gute Idee, gerade weil ``str()`` sicherlich unter den Top 10 der am meisten genutzten Builtins ist.

Verfasst: Donnerstag 9. April 2009, 19:49
von lunar
Leonidas hat geschrieben:``str`` zu überschreiben ist aber so oder so keine gute Idee, gerade weil ``str()`` sicherlich unter den Top 10 der am meisten genutzten Builtins ist.
Sicher, aber auf zwei Zeilen Code bringt das auch niemanden um ;)

Verfasst: Donnerstag 9. April 2009, 20:10
von Leonidas
lunar hat geschrieben:Sicher, aber auf zwei Zeilen Code bringt das auch niemanden um ;)
Natürlich nicht, aber als jemand der sich mehr als einmal gewundert hat warum Funktionen auf einmal nicht mehr funktionieren wird man vorsichtiger :)

Verfasst: Donnerstag 9. April 2009, 21:58
von HerrHagen
str zu überschreiben halt ich auch für unschön. Allerdings muss ich sagen das mir die ständigen Hinweise auf PEP8 doch ein wenig auf die Nerven gehen.
Gerade beim Überschreiben von builtins in der Stdlib war man auch nicht gerade zimperlich. Man schaue sich bswp. pickle.py (Py2.5, Zeile 1372) an:

Code: Alles auswählen

def loads(str):
    file = StringIO(str)
    return Unpickler(file).load()
Hier wird gleich str und file überschrieben :wink:. Und das auch mit anderen builtins an vielen anderen Stellen.

MFG HerrHagen

Verfasst: Donnerstag 9. April 2009, 22:10
von Leonidas
Die Stdlib ist ja auch kein besonders gutes Beispiel für guten Code. Oder PEP8-Konformität.

Verfasst: Donnerstag 9. April 2009, 23:13
von kbr
lunar hat geschrieben:@kbr
Man darf nicht testen, ob die Lock-Datei existiert. Man muss versuchen, sie sofort atomar zu öffnen, ansonsten existiert eine Race Condition. Dazu lässt sich die Funktion "open()" auch nicht verwenden, man muss auf "os.open()" zurückgreifen.
@lunar
Ich bin mir nicht sicher, ob ich das mit der Race Condition in diesem Fall verstanden habe. Nehmen wir an, dass os.path.exists(), open() bzw. os.open() alle atomar seien. Dennoch wird (os.)open() bei Mißerfolg nicht gleichzeitig eine Exception auslösen und eine neue Datei anlegen. D.h. die Lösung mit einem separatem Lockfile wird immer das Problem haben, dass nach einem Zugriff (mit (os.)open() oder exists()), aber vor Anlegen der Lock-Datei, der Prozeß wechselt und beide Prozesse davon ausgehen, lese- und schreib-berechtigt zu sein. D.h. wirklich zuverlässig wäre das ganze nur auf OS-Ebene zu lösen.

Verfasst: Donnerstag 9. April 2009, 23:33
von lunar
os.open ist garantiert atomar, wenn man eine Kombination aus "os.O_CREAT" und "os.O_EXCL" als Flags übergibt, d.h. die Datei wird entweder sicher neu angelegt, oder Aufruf schlägt fehl.

Verfasst: Montag 13. April 2009, 21:55
von kryz
für die suchenden: in diesem post http://www.python-forum.de/topic-18526.html habe ich das ganze file interface umgesetz