Frage

Ich habe ein urllib2 Caching-Modul, das sporadisch aufgrund des folgenden Code abstürzt:

if not os.path.exists(self.cache_location):
    os.mkdir(self.cache_location)

Das Problem wird durch die Zeit, die zweite Zeile ausgeführt wird, kann der Ordner vorhanden ist, und wird Fehler:

  File ".../cache.py", line 103, in __init__
    os.mkdir(self.cache_location)
OSError: [Errno 17] File exists: '/tmp/examplecachedir/'

Das ist, weil das Skript gleichzeitig mehrfach gestartet wird, die von Drittanbieter-Code ich keine Kontrolle habe.

Der Code (bevor ich versuchte, den Fehler zu beheben) kann hier zu finden, auf Github

kann ich nicht die tempfile.mkstemp , wie es löst die race-Bedingung durch ein zufällig benannte Verzeichnis mit ( tempfile.py Quelle hier ), die den Zweck der Cache besiegen würde.

Ich will nicht einfach, die Fehler verwerfen, da die gleichen Fehler Errno 17 Fehler ausgelöst werden, wenn der Ordnername als Datei vorhanden ist (ein anderer Fehler), zum Beispiel:

$ touch blah
$ python
>>> import os
>>> os.mkdir("blah")
Traceback (most recent call last):
  File "", line 1, in 
OSError: [Errno 17] File exists: 'blah'
>>>

Ich kann nicht mit threading.RLock als der Code von mehreren Prozessen aufgerufen wird.

So habe ich versucht, eine einfache dateibasierte Sperre Schreiben ( diese Version kann sein hier ), aber dies hat ein Problem: es schafft die lockfile eine Ebene nach oben, so /tmp/example.lock für /tmp/example/, die Pausen, wenn Sie /tmp/ als Cache-Verzeichnis verwenden (wie es versucht /tmp.lock) ..

Kurz gesagt, muss ich Cache urllib2 Antworten auf der Disc. Um dies zu tun, muß ich ein bekanntes Verzeichnis zugreifen (Erstellen es, falls erforderlich), in einem Multi-Prozess sicher. Es muss Arbeit auf OS X, Linux und Windows.

Die Gedanken? Die einzige Alternative Lösung, die ich denken kann, ist das Cache-Modul mit SQLite3 Speichern neu zu schreiben, anstatt Dateien.

War es hilfreich?

Lösung

In Python 3.x können Sie verwenden os.makedirs(path, exists_ok=True), die jede Ausnahme nicht erhöhen wird, wenn solches Verzeichnis existiert. Es wird erhöhen FileExistsError: [Errno 17], wenn eine Datei mit dem gleichen Namen wie das angeforderte Verzeichnis (path) vorhanden ist.

Stellen Sie sicher, es mit:

import os

parent = os.path.dirname(__file__)

target = os.path.join(parent, 'target')

os.makedirs(target, exist_ok=True)
os.makedirs(target, exist_ok=True)

os.rmdir(target)

with open(target, 'w'):
    pass

os.makedirs(target, exist_ok=True)

Andere Tipps

Anstelle von

if not os.path.exists(self.cache_location):
    os.mkdir(self.cache_location)

Sie tun können,

try:
    os.makedirs(self.cache_location)
except OSError:
    pass

Wie Sie mit dem gleichen enden würden Funktionalität .

HAFTUNGSAUSSCHLUSS:. Ich weiß nicht, wie Pythonic dies sein könnte


Mit SQLite3, könnte ein bisschen zu viel des Guten, aber füge hinzu, ein Los von Funktionalität und Flexibilität, um Ihren Anwendungsfall.

Wenn Sie eine Menge von „Auswahl“ zu tun haben, gleichzeitige Einsetzen und Filterung, es ist eine gute Idee zu verwenden SQLite3, da es zu viel Komplexität über einfache Dateien hinzufügen werde nicht (es könnte argumentiert werden, dass es die Komplexität entfernt).


Rereading Ihre Frage (und Kommentare) ich Ihr Problem besser verstehen kann.

Was ist die Möglichkeit, dass eine Datei könnte die gleiche Race-Bedingung erstellen?

Wenn es klein genug ist, dann würde ich so etwas wie:

if not os.path.isfile(self.cache_location):
    try:
        os.makedirs(self.cache_location)
    except OSError:
        pass

Auch der Code liest, würde ich ändern

else:
    # Our target dir is already a file, or different error,
    # relay the error!
    raise OSError(e)

else:
    # Our target dir is already a file, or different error,
    # relay the error!
    raise

wie es ist wirklich das, was Sie wollen, Python genau die gleiche Ausnahme Reraise (nur pingelig) .


Eine weitere Sache, können diese sein könnte Bedienung für Sie (Unix-only).

Der Code, den ich am Ende mit war:

import os
import errno

folder_location = "/tmp/example_dir"

try:
    os.mkdir(folder_location)
except OSError as e:
    if e.errno == errno.EEXIST and os.path.isdir(folder_location):
        # File exists, and it's a directory,
        # another process beat us to creating this dir, that's OK.
        pass
    else:
        # Our target dir exists as a file, or different error,
        # reraise the error!
        raise

Könnten Sie die Ausnahme abfangen und dann prüfen, ob die Datei vorhanden ist als Verzeichnis oder nicht?

Wenn Sie Rennbedingungen haben oft EAFP (einfacher zu fragen, Vergebung als Erlaubnis) funktioniert besser, dass LBYL (Blick, bevor Sie springen)

Fehler beim Überprüfen Strategien

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top