Locking a file in Python

后端 未结 13 2373
悲&欢浪女
悲&欢浪女 2020-11-22 03:00

I need to lock a file for writing in Python. It will be accessed from multiple Python processes at once. I have found some solutions online, but most fail for my purposes as

相关标签:
13条回答
  • 2020-11-22 03:39

    There is a cross-platform file locking module here: Portalocker

    Although as Kevin says, writing to a file from multiple processes at once is something you want to avoid if at all possible.

    If you can shoehorn your problem into a database, you could use SQLite. It supports concurrent access and handles its own locking.

    0 讨论(0)
  • 2020-11-22 03:39

    The other solutions cite a lot of external code bases. If you would prefer to do it yourself, here is some code for a cross-platform solution that uses the respective file locking tools on Linux / DOS systems.

    try:
        # Posix based file locking (Linux, Ubuntu, MacOS, etc.)
        #   Only allows locking on writable files, might cause
        #   strange results for reading.
        import fcntl, os
        def lock_file(f):
            if f.writable(): fcntl.lockf(f, fcntl.LOCK_EX)
        def unlock_file(f):
            if f.writable(): fcntl.lockf(f, fcntl.LOCK_UN)
    except ModuleNotFoundError:
        # Windows file locking
        import msvcrt, os
        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.
    # This file opener *must* be used in a "with" block.
    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
    
        # Unlock the file and close the file object.
        def __exit__(self, exc_type=None, exc_value=None, traceback=None):        
            # Flush to make sure all buffered contents are written to file.
            self.file.flush()
            os.fsync(self.file.fileno())
            # 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        
    

    Now, AtomicOpen can be used in a with block where one would normally use an open statement.

    WARNINGS:

    • If running on Windows and Python crashes before exit is called, I'm not sure what the lock behavior would be.
    • The locking provided here is advisory, not absolute. All potentially competing processes must use the "AtomicOpen" class.
    • As of (Nov 9th, 2020) this code only locks writable files on Posix systems. At some point after the posting and before this date, it became illegal to use the fcntl.lock on read-only files.
    0 讨论(0)
  • 2020-11-22 03:43

    I found a simple and worked(!) implementation from grizzled-python.

    Simple use os.open(..., O_EXCL) + os.close() didn't work on windows.

    0 讨论(0)
  • 2020-11-22 03:46

    You may find pylocker very useful. It can be used to lock a file or for locking mechanisms in general and can be accessed from multiple Python processes at once.

    If you simply want to lock a file here's how it works:

    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
    
    0 讨论(0)
  • 2020-11-22 03:52

    To add on to Evan Fossmark's answer, here's an example of how to use filelock:

    from filelock import FileLock
    
    lockfile = r"c:\scr.txt"
    lock = FileLock(lockfile + ".lock")
    with lock:
        file = open(path, "w")
        file.write("123")
        file.close()
    

    Any code within the with lock: block is thread-safe, meaning that it will be finished before another process has access to the file.

    0 讨论(0)
  • 2020-11-22 03:54

    Alright, so I ended up going with the code I wrote here, on my website link is dead, view on archive.org (also available on GitHub). I can use it in the following fashion:

    from filelock import FileLock
    
    with FileLock("myfile.txt.lock"):
        print("Lock acquired.")
        with open("myfile.txt"):
            # work with the file as it is now locked
    
    0 讨论(0)
提交回复
热议问题