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)

Simple, convenient and cross-platform file date changing library. 📝📅

Simple, convenient and cross-platform file date changing library.

kubinka0505 15 Dec 18, 2022
A python script to convert an ucompressed Gnucash XML file to a text file for Ledger and hledger.

README 1 gnucash2ledger gnucash2ledger is a Python script based on the Github Gist by nonducor (nonducor/gcash2ledger.py). This Python script will tak

Thomas Freeman 0 Jan 28, 2022
Python package to read and display segregated file names present in a directory based on type of the file

tpyfilestructure Python package to read and display segregated file names present in a directory based on type of the file. Installation You can insta

Tharun Kumar T 2 Nov 28, 2021
File-manager - A basic file manager, written in Python

File Manager A basic file manager, written in Python. Installation Install Pytho

Samuel Ko 1 Feb 5, 2022
A simple Python code that takes input from a csv file and makes it into a vcf file.

Contacts-Maker A simple Python code that takes input from a csv file and makes it into a vcf file. Imagine a college or a large community where each y

null 1 Feb 13, 2022
gitfs is a FUSE file system that fully integrates with git - Version controlled file system

gitfs is a FUSE file system that fully integrates with git. You can mount a remote repository's branch locally, and any subsequent changes made to the files will be automatically committed to the remote.

Presslabs 2.3k Jan 8, 2023
This is a file deletion program that asks you for an extension of a file (.mp3, .pdf, .docx, etc.) to delete all of the files in a dir that have that extension.

FileBulk This is a file deletion program that asks you for an extension of a file (.mp3, .pdf, .docx, etc.) to delete all of the files in a dir that h

Enoc Mena 1 Jun 26, 2022
Search for files under the specified directory. Extract the file name and file path and import them as data.

Search for files under the specified directory. Extract the file name and file path and import them as data. Based on that, search for the file, select it and open it.

G-jon FujiYama 2 Jan 10, 2022
Small-File-Explorer - I coded a small file explorer with several options

Petit explorateur de fichier / Small file explorer Pour la première option (création de répertoire) / For the first option (creation of a directory) e

Xerox 1 Jan 3, 2022
Pti-file-format - Reverse engineering the Polyend Tracker instrument file format

pti-file-format Reverse engineering the Polyend Tracker instrument file format.

Jaap Roes 14 Dec 30, 2022
Generates a clean .txt file of contents of a 3 lined csv file

Generates a clean .txt file of contents of a 3 lined csv file. File contents is the .gml file of some function which stores the contents of the csv as a map.

Alex Eckardt 1 Jan 9, 2022
PaddingZip - a tool that you can craft a zip file that contains the padding characters between the file content.

PaddingZip - a tool that you can craft a zip file that contains the padding characters between the file content.

phithon 53 Nov 7, 2022
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
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 with several blocks.

null 2 Oct 15, 2022
A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir".

the problem What directory should your app use for storing user data? If running on macOS, you should use: ~/Library/Application Support/<AppName> If

ActiveState Software 948 Dec 31, 2022
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 9, 2021
shred - A cross-platform library for securely deleting files beyond recovery.

shred Help the project financially: Donate: https://smartlegion.github.io/donate/ Yandex Money: https://yoomoney.ru/to/4100115206129186 PayPal: https:

null 4 Sep 4, 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
Simple Python File Manager

This script lets you automatically relocate files based on their extensions. Very useful from the downloads folder !

Aimé Risson 22 Dec 27, 2022