Skip to main content

A lightweight cross-platform single-instance process lock for Python.

Project description

yyds-lock

PyPI version License: MIT

中文文档

yyds-lock is an ultra-lightweight, zero-dependency Python library that guarantees single-instance execution of scripts/processes using operating system level advisory file locks. It is ideal for cron jobs, automation scripts, schedulers, and background daemons.

Key Features

  • 🛡️ Immunity to Crashes / Force Kills: Unlike simple PID files or "lock files" that leave stale markers behind if the script crashes, is killed (kill -9), or suffers power loss, yyds-lock binds the lock to the process file descriptor. The OS automatically and instantly releases the lock as soon as the process ends.
  • 🪶 Zero Dependencies: 100% pure Python standard library. The installation size is less than 5KB and does not pollute your environment.
  • 🎛️ Dual Modes: Supports both "Instant Exit" (non-blocking, terminates immediately if another instance is running) and "Queue / Wait" (blocking, waits for the existing instance to finish).
  • 💻 Cross-Platform: Seamlessly works on Linux, macOS (using fcntl.flock), and Windows (using msvcrt.locking).

Installation

pip install -U yyds-lock

Usage

You can protect your script using any of the following approaches:

Pattern A: Direct Call (Best for straightforward scripts / entrypoints)

Place this call at the very top of your entrypoint script. If another instance of the script is already running, the new instance will immediately print an error to stderr and exit with status code 1.

import time
import yyds_lock

# Force single-instance execution.
yyds_lock.force_single(lock_name="my_automation.lock", block=False)

print("Running heavy automation task...")
time.sleep(300)

Pattern B: Decorator (Best for structured functions/main entrypoints)

Decorate your main function to enforce mutual exclusion.

import yyds_lock

@yyds_lock.single_decorator(lock_name="my_task.lock", block=False)
def main():
    print("Executing single instance task safely...")

if __name__ == "__main__":
    main()

Pattern C: Handle Lock Conflict (Exception Raising)

If you prefer to handle the locking failure programmatically (e.g., to perform custom cleanups, log warnings, or run fallback logic) instead of immediately terminating the process, set raise_on_conflict=True to raise AlreadyLockedError:

import yyds_lock
from yyds_lock import AlreadyLockedError

try:
    yyds_lock.force_single(lock_name="my_automation.lock", block=False, raise_on_conflict=True)
except AlreadyLockedError:
    print("Failed to acquire lock. Running fallback script instead...")
    # Add custom fallback actions here

Configuration / Arguments

Both force_single and single_decorator accept the following arguments:

  • lock_name (str): The filename/path of the lock.
    • If a simple filename is given (e.g. "my_job.lock"), it is automatically created in the user's home directory (~).
    • If an absolute or relative path is given (e.g., "/var/run/my_job.lock"), it is created at that specific path. The parent directories will be created automatically if they do not exist.
  • block (bool):
    • False (default): Exit immediately if the lock cannot be acquired.
    • True: Block and queue, waiting for the active process to finish and release the lock.
  • raise_on_conflict (bool):
    • False (default): Immediately print an error and call sys.exit(1) when the lock is already held.
    • True: Raise AlreadyLockedError when the lock is already held, allowing the caller to catch it.

How It Works Under the Hood

  1. Linux / macOS: Uses fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) for exclusive advisory locking.
  2. Windows: Uses msvcrt.locking(fd, msvcrt.LK_NBLCK, 1) to lock the first byte of the file.
  3. The library stores the open file handles in a global dictionary inside the Python runtime. This keeps the file descriptor open and prevents garbage collection (GC) from releasing the lock prematurely.
  4. When the process terminates (normally, via Exception, sys.exit, crash, kill -9, or power failure), the OS closes the file descriptors, releasing the locks instantly.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yyds_lock-0.2.1.tar.gz (9.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yyds_lock-0.2.1-py3-none-any.whl (5.9 kB view details)

Uploaded Python 3

File details

Details for the file yyds_lock-0.2.1.tar.gz.

File metadata

  • Download URL: yyds_lock-0.2.1.tar.gz
  • Upload date:
  • Size: 9.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.11

File hashes

Hashes for yyds_lock-0.2.1.tar.gz
Algorithm Hash digest
SHA256 fc74dc294e2bf32d24132e2eb60e40a02159b1b7b93b221562c36bf0440a875f
MD5 9bf580020e558a32bbfaba05f45751da
BLAKE2b-256 144b1e316128834b2531300edc9e2d4c0c177b67f3e351bc4885c7049048e962

See more details on using hashes here.

File details

Details for the file yyds_lock-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: yyds_lock-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 5.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.11

File hashes

Hashes for yyds_lock-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 feb4dfa46c6f02e2c4e258c839f75aa0c0a0cba2fc5a47c0d02cf133e0a233a0
MD5 d034a0877a685fab61f91f9d16b89fd7
BLAKE2b-256 16e08853f9924095b7228337bdbfabd64094222622a817d8cb25904c67d169fc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page