Domanda

Ho un modulo di urllib2 caching, che si blocca sporadicamente a causa del seguente codice:

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

Il problema è che per il momento è in esecuzione la seconda linea, la cartella può esistere, e sarà errore:

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

Questo è perché lo script è contemporaneamente lanciato numerose volte, dal codice di terze parti non ho alcun controllo.

Il codice (prima ho cercato di risolvere il bug) si possono trovare qui, su github

Non riesco a utilizzare il tempfile.mkstemp , in quanto risolve la condizione di competizione utilizzando una directory di nome casuale ( tempfile.py fonte qui ), che avrebbe vanificato l'obiettivo della cache.

Non voglio scartare semplicemente l'errore, come viene generato il 17 dell'errore stesso errore Errno se è presente il nome della cartella in un file (un errore diverso), ad esempio:

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

Non riesco a utilizzare threading.RLock come il codice viene chiamato da più processi.

Così, ho provato a scrivere un semplice blocco basato su file ( quella versione può essere trovato qui ), ma questo ha un problema: si crea il file di lock up di un livello, in modo da /tmp/example.lock per /tmp/example/, che si rompe se si utilizza /tmp/ come dir cache (come si cerca di fare /tmp.lock) ..

In breve, ho bisogno di memorizzare nella cache le risposte urllib2 su disco. Per fare questo, ho bisogno di accedere ad una directory nota (creando, se necessario), in modo sicuro multiprocesso. Ha bisogno di lavorare su OS X, Linux e Windows.

Pensieri? L'unica soluzione alternativa mi viene in mente è quello di riscrivere il modulo della cache utilizzando lo storage SQLite3, piuttosto che file.

È stato utile?

Soluzione

In Python 3.x, è possibile utilizzare os.makedirs(path, exists_ok=True), che non sollevare alcuna eccezione, se esiste tale directory. Si alzerà FileExistsError: [Errno 17] se esiste un file con lo stesso nome della directory richiesta (path).

Verifica con:

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)

Altri suggerimenti

Al posto di

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

si potrebbe fare

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

Come si finirebbe con lo stesso funzionalità .

NOTA BENE: Non so come Pythonic questo potrebbe essere

.

Utilizzando SQLite3, potrebbe essere un po 'eccessivo, ma sarebbe aggiungere un molto di funzionalità e flessibilità al vostro caso d'uso.

Se si deve fare un sacco di "selezione", inserimento simultaneo e filtraggio, è una grande idea di utilizzare SQLite3, come si suole aggiungere troppa complessità sui file semplici (si potrebbe sostenere che rimuove la complessità).


Rileggendo la tua domanda (e commenti) posso capire meglio il problema.

Qual è la possibilità che un file potrebbe creare la stessa condizione di competizione?

Se è abbastanza piccola, quindi mi piacerebbe fare qualcosa di simile:

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

Inoltre, leggendo il codice, vorrei cambiare

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

a

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

come è veramente quello che vuoi, Python contro rilanciare l'esatto stessa eccezione (solo nitpicking) .


Una cosa di più, può essere questo potrebbe essere di utilizzo per voi (Unix-like solo).

Il codice ho finito con era:

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

Potrebbe intercettare l'eccezione e quindi verificare se il file esiste come una directory o no?

Quando si dispone di condizioni di gara spesso EAFP (più facile chiedere perdono che il permesso) funziona meglio che LBYL (guardare prima di saltare)

errore controllando strategie

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top