A platform independent file lock for Python

Overview

py-filelock

travis-ci

This package contains a single module, which implements a platform independent file lock in Python, which provides a simple way of inter-process communication:

from filelock import Timeout, FileLock

lock = FileLock("high_ground.txt.lock")
with lock:
    open("high_ground.txt", "a").write("You were the chosen one.")        

Don't use a FileLock to lock the file you want to write to, instead create a separate .lock file as shown above.

animated example

Similar libraries

Perhaps you are looking for something like

Installation

py-filelock is available via PyPi:

$ pip3 install filelock

Documentation

The documentation for the API is available on readthedocs.org.

Examples

A FileLock is used to indicate another process of your application that a resource or working directory is currently used. To do so, create a FileLock first:

from filelock import Timeout, FileLock

file_path = "high_ground.txt"
lock_path = "high_ground.txt.lock"

lock = FileLock(lock_path, timeout=1)

The lock object supports multiple ways for acquiring the lock, including the ones used to acquire standard Python thread locks:

with lock:
    open(file_path, "a").write("Hello there!")

lock.acquire()
try:
    open(file_path, "a").write("General Kenobi!")
finally:
    lock.release()

The acquire() method accepts also a timeout parameter. If the lock cannot be acquired within timeout seconds, a Timeout exception is raised:

try:
    with lock.acquire(timeout=10):
        open(file_path, "a").write("I have a bad feeling about this.")
except Timeout:
    print("Another instance of this application currently holds the lock.")

The lock objects are recursive locks, which means that once acquired, they will not block on successive lock requests:

def cite1():
    with lock:
        open(file_path, "a").write("I hate it when he does that.")

def cite2():
    with lock:
        open(file_path, "a").write("You don't want to sell me death sticks.")

# The lock is acquired here.
with lock:
    cite1()
    cite2()

# And released here.

FileLock vs SoftFileLock

The FileLock is platform dependent while the SoftFileLock is not. Use the FileLock if all instances of your application are running on the same host and a SoftFileLock otherwise.

The SoftFileLock only watches the existence of the lock file. This makes it ultra portable, but also more prone to dead locks if the application crashes. You can simply delete the lock file in such cases.

Contributions

Contributions are always welcome, please make sure they pass all tests before creating a pull request. Never hesitate to open a new issue, although it may take some time for me to respond.

License

This package is public domain.

Comments
  • Create lockfile on Linux with safe permissions

    Create lockfile on Linux with safe permissions

    According to the documentation of os.open the former mode was in fact the flags for the open operation. I renamed this from open_mode to open_flags and added the permission as 0o660 so that the created lockfile now has -rw-rw---- and not -rwxr-xr-x as before.

    opened by ghost 19
  • 3.3.0 breaks pylint

    3.3.0 breaks pylint

    Since the last update, I am getting the following warnings. I suspect this has to do with the fact that pylint cannot deduce that the correct instance will not be superclassed by ABC (if-else definition of classes).

    import filelock
    
    with filelock.FileLock("abc.lock"):
        print("hi")
    
    
    pylint bla.py 
    ************* Module bla
    bla.py:1:0: C0114: Missing module docstring (missing-module-docstring)
    bla.py:3:5: E0110: Abstract class 'WindowsFileLock' with abstract methods instantiated (abstract-class-instantiated)
    bla.py:3:5: E0110: Abstract class 'UnixFileLock' with abstract methods instantiated (abstract-class-instantiated)
    
    
    opened by Zahlii 11
  • Handle Unwritable Path

    Handle Unwritable Path

    Description

    If the OS cannot open a file because the path is bad, it does not make sense to repeatedly attempt to open the file as it will continue to fail, indefinitely if the -1 default timeout is used, without any feedback to the user.

    This modifies the behavior to raise the OSError/IOError exception received on Windows or Unix so FileLock exits rather than a futile infinite loop that will never succeed.

    Tests are written to demonstrate the behavior.

    opened by ooglek 10
  • Dropped support for 3.6 in 3.4.2?

    Dropped support for 3.6 in 3.4.2?

    I thought the convention was that dropping support for a Python version was a breaking change, so this should have been 4.0.0? Maybe there are other philosophies.

    opened by nedbat 8
  • Unexpected and different behaviors on Windows and Linux

    Unexpected and different behaviors on Windows and Linux

    Hi there, When I run the following script, it runs successfully on Linux but fails on Windows 10:

    import filelock
    
    lock = filelock.FileLock('test.txt')
    with lock:
        f = open('test.txt', 'w')
        f.write('hello')
        f.close()
    

    The error on Windows is:

    PermissionError: [Errno 13] Permission denied
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "lock.py", line 9, in <module>
        f.close()
    PermissionError: [Errno 13] Permission denied
    

    However, if I move f.close() outside of the context manager, the script successfully runs on Windows and Linux:

    lock = filelock.FileLock('test.txt')
    with lock:
        f = open('test.txt', 'w')
        f.write('hello')
    f.close()
    

    Why exactly must the f.close() be placed outside the context manager on Windows, but not on Linux? Is it related to msvcrt.locking()?

    I'm using filelock 2.0.7, Windows 10 (x64) and Debian Jessie (x64).

    opened by kmdouglass 8
  • Please add simple use case on README.md

    Please add simple use case on README.md

    Hi, found this from a search. Please add the 4 line usage. I expect something like this:

    LOCK_FILE = ...
    LOCK = InterprocessLock(LOCK_FILE)
    
    def foo():
        with LOCK:
            do_something()
    

    Thanks

    opened by zackees 7
  • Fix misspelled keyword argument `poll_interval` for method `acquire`

    Fix misspelled keyword argument `poll_interval` for method `acquire`

    Fix misspelled keyword argument poll_interval for method acquire. Fixes #62.

    I tried my best to add the backward compatibility layer in this PR to not break existing codebases.

    opened by XuehaiPan 7
  • Multiprocessing with FileLock fails in python 3.9

    Multiprocessing with FileLock fails in python 3.9

    On python 3.9 with filelock 3.8.0, this code hangs:

    from multiprocessing import Pool
    from filelock import FileLock
    
    
    def run(i):
        print(f"got the lock in multi process [{i}]")
    
    
    with FileLock("tmp.lock"):
        with Pool(2) as pool:
            pool.map(run, range(2))
    
    

    This is because the subprocesses tries to acquire the lock from the main process for some reason. This is not the case in older versions of python.

    This can cause many issues in python 3.9.

    For example, we use multiprocessing to run a pool of jobs and we use filelock to prevent running the same pool of job several times and avoid writing collisions.

    First reported in https://github.com/huggingface/datasets/issues/4113

    opened by lhoestq 6
  • [Request] Add option to disable logger

    [Request] Add option to disable logger

    By default this package will always print to a logger, but for my use case it only ends up cluttering the logs with "released, acquired, released, acquired, etc". I'd appreciate it if you could add the option to disable the logger :)

    help-wanted documentation 
    opened by Winning117 6
  • Update `FileLock` constructor to accept `pathlib.Path`

    Update `FileLock` constructor to accept `pathlib.Path`

    This is a small fix for people who employ pathlib.Path for their path handling. It allows a Path object to be passed to the constructor of FileLock. It is then converted to a normal str when storing it inside the object leaving the rest of the library unaffected.

    opened by f-koehler 5
  • Locking is not exclusive when two threads request for a lock at a very close interval

    Locking is not exclusive when two threads request for a lock at a very close interval

    We are using py-filelock 3.0.12 and have experienced a problem when multiple threads are trying to acquire the lock on the same file at almost the same time. This happens quite often in our AWS EC2 instance and causes data corruption. We are just following the examples in the documentation to use the module. Is there any way to avoid this?

    bug help-wanted 
    opened by fhcat 5
  • FileNotFoundError: [Errno 2] No such file or directory: '/tmp/locks/my_lock.lock'

    FileNotFoundError: [Errno 2] No such file or directory: '/tmp/locks/my_lock.lock'

    I have been intermittently seeing this error (was thinking possibly due to a race condition), but one of my coworkers started running into this issue repeatedly. Any idea why this could be happening? Reproducing solely on macOS

    Code I'm running:

    lock_path = '/tmp/locks/my_lock.lock'
    with FileLock(lock_path, timeout=0):
       ... 
    

    I see that in _acquire() the os.O_CREAT flag is being provided to os.open(), so shouldn't the file be created if it does not exist?

    Sanitized traceback:

    Traceback (most recent call last):
      File "/Users/my_user/.virtualenvs/my_proj/my_proj/cache.py", line 311, in update_cache
        with FileLock(lock_path, timeout=0):
      File "/Users/my_user/.virtualenvs/my_env/lib/python3.10/site-packages/filelock/_api.py", line 220, in __enter__
        self.acquire()
      File "/Users/my_user/.virtualenvs/my_env/lib/python3.10/site-packages/filelock/_api.py", line 173, in acquire
        self._acquire()
      File "/Users/my_user/.virtualenvs/my_env/lib/python3.10/site-packages/filelock/_unix.py", line 35, in _acquire
        fd = os.open(self._lock_file, open_mode)
    FileNotFoundError: [Errno 2] No such file or directory: '/tmp/locks/my_lock.lock'
    
    needs-more-info 
    opened by connormason 6
  • What happens if another type of OSError is raised when attempting to create a soft lock?

    What happens if another type of OSError is raised when attempting to create a soft lock?

    I ran into a strange bug when trying to lock a file on a network file-system mounted inside a container, where the lock file was created but for some reason it seems as though the file-handle wasn't properly returned. My process then got stuck waiting for the lock to be released (when it had in fact created the lock). Looking at the following code, it seems that if the OSError errno isn't EEXIST, ENOENT or EACCES, then it is assumed the file is locked

    https://github.com/tox-dev/py-filelock/blob/4730a40b87cc4b094330b2af7723658428323d60/src/filelock/_soft.py#L23-L32

    wouldn't it be more robust to do something like

     try: 
         fd = os.open(self._lock_file, mode) 
     except OSError as exception: 
         if (exception.errno == EEXIST # expected if cannot lock 
                 or (if exception.errno == EACCES and sys.platform == "win32"):  # note windows does not allow you to make a folder r/o only files 
             pass 
         else:
             raise
    

    Or do you actually want the code to keep attempting to try creating the lock on other OSErrors?

    opened by tclose 0
  • API documentation of `filelock.FileLock` is inaccurate

    API documentation of `filelock.FileLock` is inaccurate

    The API documentation of filelock.FileLock simply reads:

    alias of filelock._unix.UnixFileLock

    Naturally, this is only true on platforms supporting fcntl.flock, else it might be a WindowsFileLock or SoftFileLock. I assume that ReadTheDocs runs and produces the HTML pages on a Linux system.

    I would expect the docs to instead indicate that this is an alias for the lock implementation specific to the platform the code is being run on, which may be any of the three classes. Ideally, this would be true also for filelock.FileLock.__doc__ at runtime (e.g. for help() in the REPL).

    I'm not very familiar with Sphinx, so I don't know what the best approach for this is. My intuitive attempt would be to make FileLock a subclass of the relevant implementation (i.e. class FileLock(_FileLock) in src/filelock/__init__.py) and give it its own docstring. However, the 'Bases' line in the Sphinx-generated docs would still have to be fixed or suppressed for this particular class.

    help-wanted documentation 
    opened by JustAnotherArchivist 1
  • File permission of lock file

    File permission of lock file

    The creation of a lockfile with …

    lock = FileLock("/var/lock/foo.lock")

    … leads to these file permissions: -rwxr-xr-x

    Is there any way to prevent that the lock file becomes an executable with root ownership?

    (Version: 3.0.12-2 in Ubuntu 20.04)

    help-wanted 
    opened by ghost 6
  • Does not successfully lock on Windows

    Does not successfully lock on Windows

    Hi,

    On Windows 10.0.19041.687 Pro x64. Python 3.7.0: x64.

    Here is a test script:

    import tempfile
    import pathlib
    import threading
    from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
    from filelock import FileLock
    import time
    
    TEST_FILE = pathlib.Path(tempfile.gettempdir()) / 'test_file.txt'
    TEST_LOCK_FILE =  pathlib.Path(tempfile.gettempdir()) / 'test_file.txt.lock'
    LOCK = FileLock(TEST_LOCK_FILE)
    
    def test():
        with LOCK:
            assert TEST_FILE.read_text() == 'hi'
            TEST_FILE.write_text('')
            assert TEST_FILE.read_text() == ''
            TEST_FILE.write_text('hi')
            assert TEST_FILE.read_text() == 'hi'
            return True
    
    if __name__ == '__main__':
        print(f"Test file: {TEST_FILE}")
        print(f"Test lock file: {TEST_LOCK_FILE}")
        TEST_FILE.write_text('hi')
    
        results = []
    
        # works with ProcessPoolExecutor but not with ThreadPoolExecutor
        # It also is more likely to work if we sleep after calling submit()
        with ThreadPoolExecutor(max_workers=16) as pool:
            for i in range(100):
                if i % 10 == 0:
                    print (f"Loop: {i+1}")
                results.append(pool.submit(test))
    
            for idx, result in enumerate(results):
                print (f"Checking result: {idx + 1}")
                assert result.result() == True
    

    Example run:

    PS C:\Users\csm10495\Desktop> python .\file_lock_test.py
    Test file: C:\Users\csm10495\AppData\Local\Temp\test_file.txt
    Test lock file: C:\Users\csm10495\AppData\Local\Temp\test_file.txt.lock
    Loop: 1
    Loop: 11
    Loop: 21
    Loop: 31
    Loop: 41
    Loop: 51
    Loop: 61
    Loop: 71
    Loop: 81
    Loop: 91
    Checking result: 1
    Traceback (most recent call last):
      File ".\file_lock_test.py", line 38, in <module>
        assert result.result() == True
      File "C:\Python37\lib\concurrent\futures\_base.py", line 425, in result
        return self.__get_result()
      File "C:\Python37\lib\concurrent\futures\_base.py", line 384, in __get_result
        raise self._exception
      File "C:\Python37\lib\concurrent\futures\thread.py", line 57, in run
        result = self.fn(*self.args, **self.kwargs)
      File ".\file_lock_test.py", line 18, in test
        assert TEST_FILE.read_text() == 'hi'
    AssertionError
    

    Using a ThreadPoolExecutor seems to lead to assertion errors making me think the lock file wasn't atomically created. If I sleep a bit (like .01), after doing submit() it seems to work, but sort of defeats the purpose of the test.

    help-wanted documentation 
    opened by csm10495 5
Releases(3.9.0)
  • 3.9.0(Dec 28, 2022)

    What's Changed

    • Move to hatchling build backend by @gaborbernat in https://github.com/tox-dev/py-filelock/pull/185

    Full Changelog: https://github.com/tox-dev/py-filelock/compare/3.8.2...3.9.0

    Source code(tar.gz)
    Source code(zip)
  • 3.8.2(Dec 5, 2022)

    What's Changed

    • Bump pypa/gh-action-pypi-publish from 1.5.1 to 1.6.1 by @dependabot in https://github.com/tox-dev/py-filelock/pull/178
    • Update the license classifier to "Unlicense" by @jond01 in https://github.com/tox-dev/py-filelock/pull/180

    New Contributors

    • @jond01 made their first contribution in https://github.com/tox-dev/py-filelock/pull/180

    Full Changelog: https://github.com/tox-dev/py-filelock/compare/3.8.1...3.8.2

    Source code(tar.gz)
    Source code(zip)
  • 3.8.1(Dec 5, 2022)

    What's Changed

    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/166
    • link to flufl.lock by @dholth in https://github.com/tox-dev/py-filelock/pull/167
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/168
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/169
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/170
    • fix BaseFileLock.timeout's getter/setter being obscured by itself by @dearfl in https://github.com/tox-dev/py-filelock/pull/172
    • Fix mypy fails understanding FileLock by @gaborbernat in https://github.com/tox-dev/py-filelock/pull/177

    New Contributors

    • @dholth made their first contribution in https://github.com/tox-dev/py-filelock/pull/167
    • @dearfl made their first contribution in https://github.com/tox-dev/py-filelock/pull/172

    Full Changelog: https://github.com/tox-dev/py-filelock/compare/3.8.0...3.8.1

    Source code(tar.gz)
    Source code(zip)
  • 3.8.0(Aug 10, 2022)

    What's Changed

    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/149
    • Bump actions/upload-artifact from 2 to 3 by @dependabot in https://github.com/tox-dev/py-filelock/pull/154
    • Bump actions/download-artifact from 2 to 3 by @dependabot in https://github.com/tox-dev/py-filelock/pull/152
    • Bump pre-commit/action from 2.0.3 to 3.0.0 by @dependabot in https://github.com/tox-dev/py-filelock/pull/151
    • Bump actions/checkout from 2 to 3 by @dependabot in https://github.com/tox-dev/py-filelock/pull/153
    • Bump actions/setup-python from 2 to 4 by @dependabot in https://github.com/tox-dev/py-filelock/pull/150
    • Add timeout unit to docstrings by @jnordberg in https://github.com/tox-dev/py-filelock/pull/148
    • Unify badges style by @DeadNews in https://github.com/tox-dev/py-filelock/pull/155
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/156
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/157
    • Check 3.11 support by @gaborbernat in https://github.com/tox-dev/py-filelock/pull/158
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/159
    • Bump dependencies by @gaborbernat in https://github.com/tox-dev/py-filelock/pull/160
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/tox-dev/py-filelock/pull/162

    New Contributors

    • @dependabot made their first contribution in https://github.com/tox-dev/py-filelock/pull/154
    • @jnordberg made their first contribution in https://github.com/tox-dev/py-filelock/pull/148
    • @DeadNews made their first contribution in https://github.com/tox-dev/py-filelock/pull/155

    Full Changelog: https://github.com/tox-dev/py-filelock/compare/3.7.1...3.8.0

    Source code(tar.gz)
    Source code(zip)
  • 3.2.0(Sep 30, 2021)

A Certificate renaming tool made for IEEE CS SBC, SJCE.

PDF Batch Renamer Made for IEEE CS SBC, SJCE How to use? Before using the python script, ensure that pytesseract, pdf2image, opencv and other supporti

Ashwin Kumar U 2 Nov 14, 2021
Simple archive format designed for quickly reading some files without extracting the entire archive

Simple archive format designed for quickly reading some files without extracting the entire archive

Jarred Sumner 336 Dec 30, 2022
MetaMove is written in Python3 and aims at easing batch renaming operations based on file meta data.

MetaMove MetaMove is written in Python3 and aims at easing batch renaming operations based on file meta data. MetaMove abuses eval combined with f-str

Jan Philippi 2 Dec 28, 2021
Python Fstab Generator is a small Python script to write and generate /etc/fstab files based on yaml file on Unix-like systems.

PyFstab Generator PyFstab Generator is a small Python script to write and generate /etc/fstab files based on yaml file on Unix-like systems. NOTE : Th

Mahdi 2 Nov 09, 2021
An object-oriented approach to Python file/directory operations.

Unipath An object-oriented approach to file/directory operations Version: 1.1 Home page: https://github.com/mikeorr/Unipath Docs: https://github.com/m

Mike Orr 506 Dec 29, 2022
MHS2 Save file editing tools. Transfers save files between players, switch and pc version, encrypts and decrypts.

SaveTools MHS2 Save file editing tools. Transfers save files between players, switch and pc version, encrypts and decrypts. Credits Written by Asteris

31 Nov 17, 2022
This simple python script pcopy reads a list of file names and copies them to a separate folder

pCopy This simple python script pcopy reads a list of file names and copies them to a separate folder. Pre-requisites Python 3 (ver. 3.6) How to use

Madhuranga Rathnayake 0 Sep 03, 2021
FileGenerator - File Generator for sites that accepts documents

File Generator for sites that accepts documents This code generates files as per

Shaunak 2 Mar 19, 2022
Quick and dirty FAT12 filesystem to ZIP file converter

Quick and Dirty FAT12 Filesystem Converter This is a really crappy Python script I wrote to convert a semi-compatible FAT12 filesystem from my HP150's

Tube Time 2 Feb 12, 2022
An universal file format tool kit. At present will handle the ico format problem.

An universal file format tool kit. At present will handle the ico format problem.

Sadam·Sadik 1 Dec 26, 2021
Extract longest transcript or longest CDS transcript from GTF annotation file or gencode transcripts fasta file.

Extract longest transcript or longest CDS transcript from GTF annotation file or gencode transcripts fasta file.

laojunjun 13 Nov 23, 2022
Python function to stream unzip all the files in a ZIP archive: without loading the entire ZIP file or any of its files into memory at once

Python function to stream unzip all the files in a ZIP archive: without loading the entire ZIP file or any of its files into memory at once

Department for International Trade 206 Jan 02, 2023
Media file renamer and organizion tool

mnamer mnamer (media renamer) is an intelligent and highly configurable media organization utility. It parses media filenames for metadata, searches t

Jessy Williams 533 Dec 29, 2022
Two scripts help you to convert csv file to md file by template

Two scripts help you to convert csv file to md file by template. One help you generate multiple md files with different filenames from the first colume of csv file. Another can generate one md file w

2 Oct 15, 2022
Creates folders into a directory to categorize files in that directory by file extensions and move all things from sub-directories to current directory.

Categorize and Uncategorize Your Folders Table of Content TL;DR just take me to how to install. What are Extension Categorizer and Folder Dumper Insta

Furkan Baytekin 1 Oct 17, 2021
File storage with API access. Used as a part of the Swipio project

API File storage File storage with API access. Used as a part of the Swipio project 📝 About The Project File storage allows you to upload and downloa

25 Sep 17, 2022
Fast Python reader and editor for ASAM MDF / MF4 (Measurement Data Format) files

asammdf is a fast parser and editor for ASAM (Association for Standardization of Automation and Measuring Systems) MDF (Measurement Data Format) files

Daniel Hrisca 440 Dec 31, 2022
FUSE filesystem Python scripts for Nintendo console files

ninfs (formerly fuse-3ds) is a FUSE program to extract data from Nintendo game consoles. It works by presenting a virtual filesystem with the contents of your games, NAND, or SD card contents, and yo

Ian Burgwin 343 Jan 02, 2023
A Python script to backup your favorite Discord gifs

About the project Discord recently felt like it would be a good idea to limit the favorites to 250, which made me lose most of my gifs... Luckily for

4 Aug 03, 2022
QSynthesis is a Python3 API to perform I/O based program synthesis of bitvector expressions.

QSynthesis is a Python3 API to perform I/O based program synthesis of bitvector expressions. It aims at facilitating code deobfuscation. The algorithm is greybox approach combining both a blackbox I/

Quarkslab 103 Dec 30, 2022