Pregunta

Tengo un módulo de almacenamiento en caché urllib2, que esporádicamente se bloquea debido al código siguiente:

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

El problema es que en el momento está siendo ejecutada la segunda línea, puede existir la carpeta, y tendrá error:

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

Esto se debe a que el script se lanzó simultáneamente en numerosas ocasiones, por el código de terceros no tengo ningún control sobre.

El código (antes he tratado de corregir el error) se puede encontrar aquí, en github

No se puede utilizar el tempfile.mkstemp , ya que resuelve la condición de carrera mediante el uso de un directorio de nombre aleatorio ( tempfile.py fuente aquí ), lo que sería contrario al propósito de la caché.

Yo no quiero simplemente descartar el error, como se planteó el mismo error Errno 17 de error si el nombre de la carpeta existe como un archivo (un error diferente), por ejemplo:

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

No puedo usar threading.RLock como el código se llama a partir de múltiples procesos.

Por lo tanto, he intentado escribir una cerradura simple basada en archivos ( esa versión puede ser encontrado aquí ), pero esto tiene un problema: se crea el fichero de bloqueo de nivel uno, por lo /tmp/example.lock para /tmp/example/, que se rompe si se utiliza como memoria caché /tmp/ dir (ya que trata de hacer /tmp.lock) ..

En resumen, necesito respuestas caché urllib2 a disco. Para ello, necesito acceder a un directorio conocido (crearlo, si es necesario), de una manera segura multiproceso. Se necesita trabajar en OS X, Linux y Windows.

Los pensamientos? La solución única alternativa que se me ocurre es volver a escribir el módulo de memoria caché se utiliza el almacenamiento SQLite3, en lugar de archivos.

¿Fue útil?

Solución

En Python 3.x, puede utilizar os.makedirs(path, exists_ok=True), que no va a plantear ninguna excepción si existe tal directorio. Se levantará FileExistsError: [Errno 17] si existe un archivo con el mismo nombre que el directorio solicitado (path).

verificarlo 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)

Otros consejos

En lugar de

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

que podría hacer

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

Como era terminar con el mismo funcionalidad .

RENUNCIA:. No sé cómo esto podría ser Pythonic


El uso de SQLite3, podría ser un poco excesivo, pero añadiría un mucho de la funcionalidad y flexibilidad para su caso de uso.

Si usted tiene que hacer un montón de "seleccionar", inserción simultánea y filtrado, que es una gran idea para uso SQLite3, como no lo puedo añadir demasiada complejidad sobre los archivos simples (se podría argumentar que elimina la complejidad).


Al releer su pregunta (y comentarios) Puedo entender mejor su problema.

¿Cuál es la posibilidad de que un archivo podría crear la misma condición de carrera?

Si es lo suficientemente pequeño, entonces me gustaría hacer algo como:

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

Además, la lectura de su código, que cambiaría

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

ya que es realmente lo que quieres, Python para volver a subir exactamente la misma excepción (justo nitpicking) .


Una cosa más, puede ser esto podría ser de uso para usted (Unix solamente).

El código acabé 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

Podría detectar la excepción y luego prueba si existe el archivo como un directorio o no?

Cuando usted tiene condiciones de carrera menudo EAFP (más fácil pedir perdón que permiso) funciona mejor que LBYL (mirar antes de saltar)

estrategias de comprobación de errores

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top