textos - Bloquear un archivo en Python
leer una linea especifica de un archivo en python (12)
Necesito bloquear un archivo para escribir en Python. Se accederá desde múltiples procesos de Python a la vez. He encontrado algunas soluciones en línea, pero la mayoría fallan para mis propósitos, ya que a menudo solo están basadas en Unix o basadas en Windows.
Aquí hay un módulo de bloqueo de archivos multiplataforma: Portalocker
Aunque, como dice Kevin, escribir en un archivo de múltiples procesos a la vez es algo que desea evitar si es posible.
Si puede calzar su problema en una base de datos, puede usar SQLite. Admite acceso simultáneo y maneja su propio bloqueo.
El escenario es así: el usuario solicita un archivo para hacer algo. Luego, si el usuario envía nuevamente la misma solicitud, informa al usuario que la segunda solicitud no finaliza hasta que finaliza la primera solicitud. Es por eso que utilizo el mecanismo de bloqueo para manejar este problema.
Aquí está mi código de trabajo:
from lockfile import LockFile
lock = LockFile(lock_file_path)
status = ""
if not lock.is_locked():
lock.acquire()
status = lock.path + '' is locked.''
print status
else:
status = lock.path + " is already locked."
print status
return status
El bloqueo de un archivo suele ser una operación específica de la plataforma, por lo que es posible que deba tener en cuenta la posibilidad de ejecutar en diferentes sistemas operativos. Por ejemplo:
import os
def my_lock(f):
if os.name == "posix":
# Unix or OS X specific locking here
elif os.name == "nt":
# Windows specific locking here
else:
print "Unknown operating system, lock unavailable"
Encontré una implementation simple y trabajada (!) De grizzled-python.
Uso simple os.open (..., O_EXCL) + os.close () no funcionó en Windows.
He estado buscando varias soluciones para hacer eso y mi elección ha sido oslo.concurrency
Es poderoso y relativamente bien documentado. Está basado en sujetadores.
Otras soluciones
- Portalocker : requiere pywin32, que es una instalación exe, por lo que no es posible a través de pip
- fasteners : poco documentados
- lockfile : obsoleto
- flufl.lock : bloqueo de archivos seguro para NFS para sistemas POSIX.
- simpleflock : Última actualización 2013-07
- zc.lockfile : última actualización 2016-06 (a partir de 2017-03)
- lock_file : Última actualización en 2007-10
He estado trabajando en una situación como esta en la que ejecuto varias copias del mismo programa desde el mismo directorio / carpeta y errores de registro. Mi enfoque fue escribir un "archivo de bloqueo" en el disco antes de abrir el archivo de registro. El programa verifica la presencia del "archivo de bloqueo" antes de continuar y espera su turno si existe el "archivo de bloqueo".
Aquí está el código:
def errlogger(error):
while True:
if not exists(''errloglock''):
lock = open(''errloglock'', ''w'')
if exists(''errorlog''): log = open(''errorlog'', ''a'')
else: log = open(''errorlog'', ''w'')
log.write(str(datetime.utcnow())[0:-7] + '' '' + error + ''/n'')
log.close()
remove(''errloglock'')
return
else:
check = stat(''errloglock'')
if time() - check.st_ctime > 0.01: remove(''errloglock'')
print(''waiting my turn'')
EDITAR --- Después de pensar en algunos de los comentarios sobre bloqueos obsoletos anteriores, edité el código para agregar una verificación de estado obsoleto del "archivo de bloqueo". La sincronización de varios miles de iteraciones de esta función en mi sistema dio un promedio de 0.002066 ... segundos desde el momento anterior:
lock = open(''errloglock'', ''w'')
para justo después:
remove(''errloglock'')
así que pensé que comenzaría con 5 veces esa cantidad para indicar la pérdida de energía y controlar la situación en busca de problemas.
Además, cuando estaba trabajando con el tiempo, me di cuenta de que tenía un poco de código que no era realmente necesario:
lock.close()
que tuve inmediatamente después de la declaración abierta, por lo que la eliminé en esta edición.
La coordinación del acceso a un único archivo en el nivel del sistema operativo está plagada de todo tipo de problemas que probablemente no desee resolver.
Su mejor opción es tener un proceso separado que coordine el acceso de lectura / escritura a ese archivo.
Las otras soluciones citan una gran cantidad de bases de código externo. Si prefiere hacerlo usted mismo, aquí hay un código para una solución multiplataforma que utiliza las herramientas de bloqueo de archivos respectivas en sistemas Linux / DOS.
try:
# Posix based file locking (Linux, Ubuntu, MacOS, etc.)
import fcntl
def lock_file(f):
fcntl.lockf(f, fcntl.LOCK_EX)
def unlock_file(f): pass
except ModuleNotFoundError:
# Windows file locking
import msvcrt
def file_size(f):
return os.path.getsize( os.path.realpath(f.name) )
def lock_file(f):
msvcrt.locking(f.fileno(), msvcrt.LK_RLCK, file_size(f))
def unlock_file(f):
msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, file_size(f))
# Class for ensuring that all file operations are atomic, treat
# initialization like a standard call to ''open'' that happens to be atomic
class AtomicOpen:
# Open the file with arguments provided by user. Then acquire
# a lock on that file object (WARNING: Advisory locking)
def __init__(self, path, *args, **kwargs):
# Open the file and acquire a lock on the file before operating
self.file = open(path,*args, **kwargs)
# Lock the opened file
lock_file(self.file)
# Return the opened file object (knowing a lock has been obtained)
def __enter__(self, *args, **kwargs): return self.file
# Allows users to use the ''close'' function if they want, in case
# the user did not have the AtomicOpen in a "with" block.
def close(self): self.__exit__()
# Unlock the file and close the file object
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
# Release the lock on the file
unlock_file(self.file)
self.file.close()
# Handle exceptions that may have come up during execution, by
# default any exceptions are raised to the user
if (exc_type != None): return False
else: return True
Ahora, "AtomicOpen" podría usarse donde sea que uno normalmente usaría una declaración "abierta".
ADVERTENCIA: si se ejecuta en Windows y Python falla antes de que se llame a exit , no estoy seguro de cuál sería el comportamiento de bloqueo.
ADVERTENCIA: El bloqueo proporcionado aquí es orientativo, no absoluto. Todos los procesos potencialmente competidores deben usar la clase "AtomicOpen".
Locking es específico de la plataforma y del dispositivo, pero en general, tiene algunas opciones:
- Use flock (), o equivalente (si su sistema operativo lo admite). Esto es bloqueo de aviso, a menos que compruebe el bloqueo, se ignora.
- Utilice una metodología de bloqueo-copia-movimiento-desbloqueo, donde copie el archivo, escriba los datos nuevos, luego muévalo (muévase, no copie, muévase es una operación atómica en Linux, verifique su sistema operativo) y compruebe existencia del archivo de bloqueo
- Use un directorio como un "candado". Esto es necesario si está escribiendo en NFS, ya que NFS no es compatible con flock ().
- También existe la posibilidad de usar memoria compartida entre los procesos, pero nunca lo he intentado; es muy específico del sistema operativo.
Para todos estos métodos, deberá usar una técnica de bloqueo de giro (reintentar después de fallar) para adquirir y probar el bloqueo. Esto deja una pequeña ventana para una mala sincronización, pero en general es lo suficientemente pequeña como para no ser un problema importante.
Si está buscando una solución que sea multiplataforma, entonces es mejor que inicie sesión en otro sistema a través de algún otro mecanismo (la siguiente mejor opción es la técnica NFS anterior).
Tenga en cuenta que sqlite está sujeto a las mismas restricciones sobre NFS que los archivos normales, por lo que no puede escribir en una base de datos sqlite en un recurso compartido de red y obtener la sincronización de forma gratuita.
Muy bien, así que terminé yendo con el código que escribí aquí, en el enlace de mi sitio web está muerto, ver en archive.org ( también disponible en GitHub ). Puedo usarlo de la siguiente manera:
from filelock import FileLock
with FileLock("myfile.txt"):
# work with the file as it is now locked
print("Lock acquired.")
Prefiero el archivo de bloqueo: bloqueo de archivos independiente de la plataforma
Puede encontrar pylocker muy útil. Se puede usar para bloquear un archivo o para bloquear mecanismos en general y se puede acceder desde múltiples procesos de Python a la vez.
Si simplemente desea bloquear un archivo, así es cómo funciona:
import uuid
from pylocker import Locker
# create a unique lock pass. This can be any string.
lpass = str(uuid.uuid1())
# create locker instance.
FL = Locker(filePath=''myfile.txt'', lockPass=lpass, mode=''w'')
# aquire the lock
with FL as r:
# get the result
acquired, code, fd = r
# check if aquired.
if fd is not None:
print fd
fd.write("I have succesfuly aquired the lock !")
# no need to release anything or to close the file descriptor,
# with statement takes care of that. let''s print fd and verify that.
print fd