Skip to main content

redis-locking

PyPI Python Versions

redis-locking is a small, practical layer for named Redis locks.

from redis import Redis
from redis_locking import LockSettings, acquire_lock, build_key

redis_conn = Redis()
locks = LockSettings(key_prefix="billing:", lock_expire=120, acquire_timeout=10)

with acquire_lock(redis_conn, build_key("invoice", 42), settings=locks):
    send_invoice(42)

Redis already has good locking primitives, and python-redis-lock handles the hard part of acquiring and releasing locks safely. This package focuses on the application code around it: namespacing, consistent settings, decorators, local lock disabling, and the occasional extra check that some device or external process is ready.

It is intentionally boring, which is useful when the alternative is twelve slightly different lock helpers spread across a codebase, all with their own timeout defaults and naming conventions.

Installing

$ python -m pip install redis-locking

redis-locking supports Python 3.8+ and uses python-redis-lock as its backend.

A small example

from redis import Redis
from redis_locking import LockSettings, acquire_lock, is_locked

redis_conn = Redis.from_url("redis://localhost:6379/0")
settings = LockSettings(key_prefix="worker:", lock_expire=120, acquire_timeout=5)

if is_locked(redis_conn, "daily-sync", settings=settings):
    print("Someone else is already running this")

with acquire_lock(redis_conn, "daily-sync", settings=settings):
    run_daily_sync()

By default, acquire_lock waits up to acquire_timeout seconds. Set blocking=False when the worker should skip work that is already running:

from redis_locking import LockAcquireError

try:
    with acquire_lock(redis_conn, "daily-sync", settings=settings, blocking=False):
        run_daily_sync()
except LockAcquireError:
    print("Skipped, lock is already held")

Lock settings

Most services are easier to reason about when lock behavior is configured once, then reused everywhere.

from redis_locking import LockSettings

LOCKS = LockSettings(
    key_prefix="api:",
    acquire_timeout=10,
    lock_expire=120,
    auto_renewal=True,
)

Available settings:

  • key_prefix: prefix all lock keys for a service or environment.
  • acquire_timeout: maximum seconds to wait when acquiring a blocking lock.
  • lock_expire: Redis expiry for the lock key.
  • blocking: wait for the lock by default, or fail immediately.
  • auto_renewal: let python-redis-lock renew the lock while work is running.
  • disabled: bypass locks, useful for local development, tests, or one-off scripts.

Busy checks

Sometimes the Redis key is only part of the story. You might lock a device, gateway, or job runner, then still need to ask it whether it is actually ready; lock_key puts that extra check behind the same context manager.

from typing import Optional

from redis_locking import lock_key


def is_gateway_busy(lock_name: str, timeout: Optional[int]) -> bool:
    return gateway_client.is_busy(lock_name, timeout=timeout)


with lock_key(
    redis_conn,
    "gateway:42",
    settings=LOCKS,
    check_fn=is_gateway_busy,
    check_retries=2,
    check_delay=5,
    check_timeout=30,
):
    update_gateway(42)

Because the check runs after the Redis lock is acquired, only one worker asks the external resource at a time. If the check keeps reporting busy, LockBusyError is raised and the Redis lock is released.

Decorators

For jobs where the lock name is static, the decorator keeps the call site small.

from redis_locking import locked


@locked(redis_conn, "reports:nightly", settings=LOCKS)
def build_nightly_report():
    generate_report()

Domain wrappers

The best use of this package is usually one layer deeper. Define names once, then use those wrappers everywhere.

from typing import Optional

from redis_locking import LockSettings, acquire_lock, build_key, is_locked

PROCESS_LOCKS = LockSettings(key_prefix="orders:", lock_expire=120, auto_renewal=True)


def lock_process(redis_conn, process_name: str, entity_id: Optional[int] = None):
    key = build_key("process", process_name, entity_id)
    return acquire_lock(redis_conn, key, settings=PROCESS_LOCKS, blocking=False)


def is_process_locked(redis_conn, process_name: str, entity_id: Optional[int] = None) -> bool:
    key = build_key("process", process_name, entity_id)
    return is_locked(redis_conn, key, settings=PROCESS_LOCKS)

Now the rest of the application does not have to remember key prefixes, expiry values, or whether a particular lock should block. It just calls lock_process and gets on with the work.

API

from redis_locking import (
    LockAcquireError,
    LockBusyError,
    LockCheckError,
    LockSettings,
    acquire_lock,
    build_key,
    is_locked,
    locked,
    lock_key,
)

Development

$ python -m venv .venv
$ . .venv/bin/activate
$ python -m pip install -U pip
$ python -m pip install -e ".[dev]"
$ pytest

Release files for redis-locking 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for redis-locking 0.1.1
File Size Uploaded
redis_locking-0.1.1.tar.gz 7.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for redis-locking 0.1.1
File Interpreter ABI Platform
redis_locking-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 14.0 kB

Release files / redis_locking-0.1.1.tar.gz

Download URL redis_locking-0.1.1.tar.gz
Size 7.8 kB
Tags Source
SHA-256 checksum
How to use checksums
58084eba0c60f7d3992b29820048d2403b4ec4935d015293769f51a711b58568
BLAKE2b-256 checksum
How to use checksums
8c575c4339cc14625fa866fcd317a09b964db108444b12136dd858efcdfe41b5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 19, 2026.

Transparency log

Release files / redis_locking-0.1.1-py3-none-any.whl

Download URL redis_locking-0.1.1-py3-none-any.whl
Size 6.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
30c932d0cff6dc50f34353d091c7cb1ac1f29c5187addfdbebcb7ab8c21cb8d4
BLAKE2b-256 checksum
How to use checksums
d77030385edb5c8b639f637bdaa39ea7a2edaf392a33f53d148cefc9f0bbf806
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page