una txt partir lista linea leer importar guardar especifica español dat crear como binarios archivos archivo abrir python file locking

txt - readline python



¿Cuál es la mejor manera de abrir un archivo para acceso exclusivo en Python? (6)

La solución debería funcionar dentro del mismo proceso (como en el ejemplo anterior) y también cuando otro proceso haya abierto el archivo.

Si con ''otro proceso'' quiere decir ''cualquier proceso'' (es decir, no su programa), en Linux no hay forma de lograr esto confiando únicamente en las llamadas al sistema ( fcntl & friends). Lo que quiere es un bloqueo obligatorio , y la forma de Linux para obtenerlo es un poco más complicada:

Reinstala la partición que contiene tu archivo con la opción mand :

# mount -o remount,mand /dev/hdXY

Establezca el indicador sgid para su archivo:

# chmod g-x,g+s yourfile

En su código de Python, obtenga un bloqueo exclusivo en ese archivo:

fcntl.flock(fd, fcntl.LOCK_EX)

Ahora incluso cat no podrá leer el archivo hasta que suelte el bloqueo.

¿Cuál es la forma más elegante de resolver esto?

  • abra un archivo para leer, pero solo si no está abierto para escribir
  • abra un archivo para escribir, pero solo si no está abierto para leer o escribir

Las funciones integradas funcionan así

>>> path = r"c:/scr.txt" >>> file1 = open(path, "w") >>> print file1 <open file ''c:/scr.txt'', mode ''w'' at 0x019F88D8> >>> file2 = open(path, "w") >>> print file2 <open file ''c:/scr.txt'', mode ''w'' at 0x02332188> >>> file1.write("111") >>> file2.write("222") >>> file1.close()

scr.txt ahora contiene ''111''.

>>> file2.close()

scr.txt fue sobrescrito y ahora contiene ''222'' (en Windows, Python 2.4).

La solución debería funcionar dentro del mismo proceso (como en el ejemplo anterior) y también cuando otro proceso haya abierto el archivo.
Se prefiere, si un programa bloqueado no mantiene el bloqueo abierto.


Aquí hay un comienzo en la mitad de una implementación portátil de win32, que no necesita un mecanismo de bloqueo separado.

Requiere las extensiones de Python para Windows para llegar a la API de Win32, pero eso ya es casi obligatorio para python en Windows, y alternativamente puede hacerse con ctypes . El código podría adaptarse para exponer más funcionalidades si es necesario (como permitir FILE_SHARE_READ lugar de no compartir nada). Consulte también la documentación de MSDN para las llamadas al sistema WriteFile y WriteFile , y el artículo sobre Creación y apertura de archivos .

Como se ha mencionado, puede usar el módulo fcntl estándar para implementar la mitad de esto de unix, si es necesario.

import winerror, pywintypes, win32file class LockError(StandardError): pass class WriteLockedFile(object): """ Using win32 api to achieve something similar to file(path, ''wb'') Could be adapted to handle other modes as well. """ def __init__(self, path): try: self._handle = win32file.CreateFile( path, win32file.GENERIC_WRITE, 0, None, win32file.OPEN_ALWAYS, win32file.FILE_ATTRIBUTE_NORMAL, None) except pywintypes.error, e: if e[0] == winerror.ERROR_SHARING_VIOLATION: raise LockError(e[2]) raise def close(self): self._handle.close() def write(self, str): win32file.WriteFile(self._handle, str)

Así es como se comporta su ejemplo anterior:

>>> path = "C://scr.txt" >>> file1 = WriteLockedFile(path) >>> file2 = WriteLockedFile(path) #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... LockError: ... >>> file1.write("111") >>> file1.close() >>> print file(path).read() 111


EDIT: ¡ Lo resolví yo mismo! ¡Utilizando la existencia de directorios y la edad como un mecanismo de bloqueo! Bloquear por archivo es seguro solo en Windows (porque Linux sobrescribe silenciosamente), pero el bloqueo por directorio funciona perfectamente tanto en Linux como en Windows. Ver mi GIT donde creé una clase fácil de usar ''lockbydir.DLock'' para eso:

https://github.com/drandreaskrueger/lockbydir

En la parte inferior del archivo léame, encontrarás 3 GITplayers donde puedes ver los ejemplos de código ejecutados en vivo en tu navegador. Genial, ¿no? :-)

Gracias por tu atención

Esta fue mi pregunta original:

Me gustaría responder a parity3 ( https://meta..com/users/1454536/parity3 ) pero no puedo comentar directamente (''Debes tener 50 reputación para comentar''), ni veo ninguna forma de contactarme él / ella directamente. ¿Qué me sugieres para llegar a él?

Mi pregunta:

He implementado algo similar a lo que parity3 sugirió aquí como respuesta: https://.com/a/21444311/3693375 ("Asumiendo tu intérprete de Python, y el ...")

Y funciona brillantemente - en Windows. (Lo estoy usando para implementar un mecanismo de bloqueo que funciona en procesos iniciados independientemente. https://github.com/drandreaskrueger/lockbyfile )

Pero aparte de parity3 dice, NO funciona igual en Linux:

os.rename (src, dst)

Cambie el nombre del archivo o directorio src a dst. ... En Unix, si existe dst y es un archivo, se reemplazará en silencio si el usuario tiene permiso. La operación puede fallar en algunos sabores de Unix si src y dst están en diferentes sistemas de archivos. Si tiene éxito, el cambio de nombre será una operación atómica (este es un requisito POSIX). En Windows, si ya existe, OSError se generará ( https://docs.python.org/2/library/os.html#os.rename )

El reemplazo silencioso es el problema. En Linux. El "si ya existe dst, OSError se levantará" es ideal para mis propósitos. Pero solo en Windows, lamentablemente.

Supongo que el ejemplo de parity3 sigue funcionando la mayor parte del tiempo, debido a su condición if

if not os.path.exists(lock_filename): try: os.rename(tmp_filename,lock_filename)

Pero luego todo el asunto ya no es atómico.

Porque la condición if podría ser verdadera en dos procesos paralelos, y luego ambos cambiarán de nombre, pero solo uno ganará la carrera de cambio de nombre. Y ninguna excepción planteada (en Linux).

¿Alguna sugerencia? ¡Gracias!

PD: Sé que esta no es la manera correcta, pero me falta una alternativa. POR FAVOR, no me castigue con rebajar mi reputación. Miré a mi alrededor, para resolverlo yo mismo. ¿Cómo PM a los usuarios aquí? Y ¿por qué no puedo?


No creo que haya una forma completamente cruzada. En Unix, el módulo fcntl hará esto por usted. Sin embargo, en Windows (que supongo que es por las rutas), tendrá que utilizar el módulo win32file.

Afortunadamente, hay una implementación portátil ( portalocker ) que utiliza el método apropiado de la plataforma en el libro de cocina de Python.

Para usarlo, abra el archivo, y luego llame:

portalocker.lock(file, flags)

donde los indicadores son portalocker.LOCK_EX para acceso de escritura exclusivo, o LOCK_SH para acceso de lectura compartido.


Para que estés seguro al abrir archivos dentro de una aplicación, puedes intentar algo como esto:

import time class ExclusiveFile(file): openFiles = {} fileLocks = [] class FileNotExclusiveException(Exception): pass def __init__(self, *args): sMode = ''r'' sFileName = args[0] try: sMode = args[1] except: pass while sFileName in ExclusiveFile.fileLocks: time.sleep(1) ExclusiveFile.fileLocks.append(sFileName) if not sFileName in ExclusiveFile.openFiles.keys() or (ExclusiveFile.openFiles[sFileName] == ''r'' and sMode == ''r''): ExclusiveFile.openFiles[sFileName] = sMode try: file.__init__(self, sFileName, sMode) finally: ExclusiveFile.fileLocks.remove(sFileName) else: ExclusiveFile.fileLocks.remove(sFileName) raise self.FileNotExclusiveException(sFileName) def close(self): del ExclusiveFile.openFiles[self.name] file.close(self)

De esta forma subclasifica la clase de file . Ahora solo hazlo:

>>> f = ExclusiveFile(''/tmp/a.txt'', ''r'') >>> f <open file ''/tmp/a.txt'', mode ''r'' at 0xb7d7cc8c> >>> f1 = ExclusiveFile(''/tmp/a.txt'', ''r'') >>> f1 <open file ''/tmp/a.txt'', mode ''r'' at 0xb7d7c814> >>> f2 = ExclusiveFile(''/tmp/a.txt'', ''w'') # can''t open it for writing now exclfile.FileNotExclusiveException: /tmp/a.txt

Si lo abres primero con el modo ''w'', no permitirá más aperturas, incluso en modo lectura, tal como querías ...


Suponiendo que su intérprete de Python, y el sistema operativo y el sistema de archivos subyacentes tratan a os.rename como una operación atómica y se producirá un error cuando existe el destino, el siguiente método está libre de condiciones de carrera. Estoy usando esto en producción en una máquina Linux. No requiere libs de terceros y no depende del sistema operativo, y aparte de crear un archivo adicional, el rendimiento alcanzado es aceptable para muchos casos de uso. Puede aplicar fácilmente el patrón de decorador de funciones de python o un gestor de contexto ''with_statement'' aquí para abstraer el desorden.

Tendrá que asegurarse de que lock_filename no exista antes de que comience un nuevo proceso / tarea.

import os,time def get_tmp_file(): filename=''tmp_%s_%s''%(os.getpid(),time.time()) open(filename).close() return filename def do_exclusive_work(): print ''exclusive work being done...'' num_tries=10 wait_time=10 lock_filename=''filename.lock'' acquired=False for try_num in xrange(num_tries): tmp_filename=get_tmp_file() if not os.path.exists(lock_filename): try: os.rename(tmp_filename,lock_filename) acquired=True except (OSError,ValueError,IOError), e: pass if acquired: try: do_exclusive_work() finally: os.remove(lock_filename) break os.remove(tmp_filename) time.sleep(wait_time) assert acquired, ''maximum tries reached, failed to acquire lock file''

EDITAR

Ha salido a la luz que os.rename sobrescribe silenciosamente el destino en un sistema operativo que no es de Windows. Gracias por señalar esto @ akrueger!

Aquí hay una solución, recopilada desde here :

En lugar de usar os.rename puedes usar:

try: if os.name != ''nt'': # non-windows needs a create-exclusive operation fd = os.open(lock_filename, os.O_WRONLY | os.O_CREAT | os.O_EXCL) os.close(fd) # non-windows os.rename will overwrite lock_filename silently. # We leave this call in here just so the tmp file is deleted but it could be refactored so the tmp file is never even generated for a non-windows OS os.rename(tmp_filename,lock_filename) acquired=True except (OSError,ValueError,IOError), e: if os.name != ''nt'' and not ''File exists'' in str(e): raise

@ akrueger Probablemente estés bien con tu solución basada en directorio, simplemente dándote un método alternativo.