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)

ValveVMF - A python library to parse Valve's VMF files

ValveVMF ValveVMF is a Python library for parsing .vmf files for the Source Engi

pySourceSDK 2 Jan 02, 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
Listreqs is a simple requirements.txt generator. It's an alternative to pipreqs

⚡ Listreqs Listreqs is a simple requirements.txt generator. It's an alternative to pipreqs. Where in Pipreqs, it helps you to Generate requirements.tx

Soumyadip Sarkar 4 Oct 15, 2021
Remove [x]_ from StudIP zip Archives and archive_filelist.csv completely

This tool removes the "[x]_" at the beginning of StudIP zip Archives. It also deletes the "archive_filelist.csv" file

Kelke vl 1 Jan 19, 2022
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
ZipFly is a zip archive generator based on zipfile.py

ZipFly is a zip archive generator based on zipfile.py. It was created by Buzon.io to generate very large ZIP archives for immediate sending out to clients, or for writing large ZIP archives without m

Buzon 506 Jan 04, 2023
A JupyterLab extension that allows opening files and directories with external desktop applications.

A JupyterLab extension that allows opening files and directories with external desktop applications.

martinRenou 0 Oct 14, 2021
Python script for converting figma produced SVG files into C++ JUCE framework source code

AutoJucer Python script for converting figma produced SVG files into C++ JUCE framework source code Watch the tutorial here! Getting Started Make some

SuperConductor 1 Nov 26, 2021
Copy only text-like files from the folder

copy-only-text-like-files-from-folder-python copy only text-like files from the folder This project is for those who want to copy only source code or

1 May 17, 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
Utils for streaming large files (S3, HDFS, gzip, bz2...)

smart_open — utils for streaming large files in Python What? smart_open is a Python 3 library for efficient streaming of very large files from/to stor

RARE Technologies 2.7k Jan 06, 2023
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
Object-oriented file system path manipulation

path (aka path pie, formerly path.py) implements path objects as first-class entities, allowing common operations on files to be invoked on those path

Jason R. Coombs 1k Dec 28, 2022
Python virtual filesystem for SQLite to read from and write to S3

Python virtual filesystem for SQLite to read from and write to S3

Department for International Trade 70 Jan 04, 2023
Pure Python tools for reading and writing all TIFF IFDs, sub-IFDs, and tags.

Tiff Tools Pure Python tools for reading and writing all TIFF IFDs, sub-IFDs, and tags. Developed by Kitware, Inc. with funding from The National Canc

Digital Slide Archive 32 Dec 14, 2022
dotsend is a web application which helps you to upload your large files and share file via link

dotsend is a web application which helps you to upload your large files and share file via link

Devocoe 0 Dec 03, 2022
Python interface for reading and appending tar files

Python interface for reading and appending tar files, while keeping a fast index for finding and reading files in the archive. This interface has been

Lawrence Livermore National Laboratory 1 Nov 12, 2021
File support for asyncio

aiofiles: file support for asyncio aiofiles is an Apache2 licensed library, written in Python, for handling local disk files in asyncio applications.

Tin Tvrtković 2.1k Jan 01, 2023
Powerful Python library for atomic file writes.

Powerful Python library for atomic file writes.

Markus Unterwaditzer 313 Oct 19, 2022
BOOTH宛先印刷用CSVから色々な便利なリストを作成してCSVで出力するプログラムです。

BOOTH注文リスト作成スクリプト このPythonスクリプトは、BOOTHの「宛名印刷用CSV」から、 未発送の注文 今月の注文 特定期間の注文 を抽出した上で、各注文を商品毎に一覧化したCSVとして出力するスクリプトです。 簡単な使い方 ダウンロード 通常は、Relaseから、booth_ord

hinananoha 1 Nov 28, 2021