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)

useful files for the Freenove Big Hexapod

FreenoveBigHexapod useful files for the Freenove Big Hexapod HexaDogPos is a utility for converting the Freenove xyz co-ordinate system to servo angle

Alex 2 May 28, 2022
Python's Filesystem abstraction layer

PyFilesystem2 Python's Filesystem abstraction layer. Documentation Wiki API Documentation GitHub Repository Blog Introduction Think of PyFilesystem's

pyFilesystem 1.8k Jan 02, 2023
CleverCSV is a Python package for handling messy CSV files.

CleverCSV is a Python package for handling messy CSV files. It provides a drop-in replacement for the builtin CSV module with improved dialect detection, and comes with a handy command line applicati

The Alan Turing Institute 1k Dec 19, 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
BREP : Binary Search in plaintext and gzip files

BREP : Binary Search in plaintext and gzip files Search large files in O(log n) time using binary search. We support plaintext and Gzipped files. Benc

Arnaud de Saint Meloir 5 Dec 24, 2021
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
Maltego transforms to pivot between PE files based on their VirusTotal codeblocks

VirusTotal Codeblocks Maltego Transforms Introduction These Maltego transforms allow you to pivot between different PE files based on codeblocks they

Ariel Jungheit 18 Feb 03, 2022
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
Vericopy - This Python script provides various usage modes for secure local file copying and hashing.

Vericopy This Python script provides various usage modes for secure local file copying and hashing. Hash data is captured and logged for paths before

15 Nov 05, 2022
Various technical documentation, in electronically parseable format

a-pile-of-documentation Various technical documentation, in electronically parseable format. You will need Python 3 to run the scripts and programs in

Jonathan Campbell 2 Nov 20, 2022
An easy-to-use library for emulating code in minidump files.

dumpulator Note: This is a work-in-progress prototype, please treat it as such. An easy-to-use library for emulating code in minidump files. Example T

Duncan Ogilvie 362 Dec 31, 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
Python library for reading and writing tabular data via streams.

tabulator-py A library for reading and writing tabular data (csv/xls/json/etc). [Important Notice] We have released Frictionless Framework. This frame

Frictionless Data 231 Dec 09, 2022
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
A Python library that provides basic functions to read / write Aseprite format files

A Python library that provides basic functions to read / write Aseprite format files

Joe Trewin 1 Jan 13, 2022
Yadl - it is a simple library for working with both dotenv files and environment variables.

Yadl Yadl - it is a simple library for working with both dotenv files and environment variables. Features Validation of whitespaces. Validation of num

Ivan Kapranov 3 Oct 19, 2021
Dragon Age: Origins toolset to extract/build .erf files, patch language-specific .dlg files, and view the contents of files in the ERF or GFF format

DAOTools This is a set of tools for Dragon Age: Origins modding. It can patch the text lines of .dlg files, extract and build an .erf file, and view t

8 Dec 06, 2022
Publicly Open Amazon AWS S3 Bucket Viewer

S3Viewer Publicly open storage viewer (Amazon S3 Bucket, Azure Blob, FTP server, HTTP Index Of/) s3viewer is a free tool for security researchers that

Sharon Brizinov 377 Dec 02, 2022
Lumar - Smart File Creator

Lumar is a free tool for creating and managing files. With Lumar you can quickly create any type of file, add a file content and file size. With Lumar you can also find out if Photoshop or other imag

Paul - FloatDesign 3 Dec 10, 2021