sqlalchemy_dlock.lock.base module

class sqlalchemy_dlock.lock.base.AbstractLockMixin(*, key, convert=None, **kwargs)[source]

Bases: Generic[KeyTV, ActualKeyTV], ABC

Parameters:
  • key (KeyTV)

  • convert (Callable[[KeyTV], ActualKeyTV] | None)

property actual_key: ActualKeyTV
abstractmethod get_actual_key()[source]
Return type:

TypeVar(ActualKeyTV)

class sqlalchemy_dlock.lock.base.BaseAsyncSadLock(connection_or_session, key, /, contextual_timeout=None, **kwargs)[source]

Bases: AbstractLockMixin, Generic[KeyTV, AsyncConnectionTV], _local, ABC

Async version of BaseSadLock

Parameters:
  • connection_or_session (AsyncConnectionTV)

  • key (KeyTV)

  • contextual_timeout (float | int | None)

final async acquire(block=True, timeout=None, *args, **kwargs)[source]
Return type:

bool

Parameters:
final async close(*args, **kwargs)[source]
property connection_or_session: AsyncConnectionTV
abstractmethod async do_acquire(block=True, timeout=None, *args, **kwargs)[source]
Return type:

bool

Parameters:
abstractmethod async do_release(*args, **kwargs)[source]
property key: KeyTV
property locked: bool
final async release(*args, **kwargs)[source]
class sqlalchemy_dlock.lock.base.BaseSadLock(connection_or_session, key, /, contextual_timeout=None, **kwargs)[source]

Bases: AbstractLockMixin, Generic[KeyTV, ConnectionTV], _local, ABC

Base class of database lock implementation

Note

  • It’s Thread-Local (threading.local)

  • It’s an abstract class, do not manual instantiate

The acquire() and release() methods can be used as context managers for a with statement. acquire() will be called when the block is entered, and release() will be called when the block is exited. Hence, the following snippet:

with some_lock:
    # do something...
    pass

is equivalent to:

some_lock.acquire()
try:
    # do something...
    pass
finally:
    some_lock.release()

Note

A TimeoutError will be thrown if acquire timeout in with statement.

Parameters:
  • connection_or_session (TypeVar(ConnectionTV, bound= Connection | Session | scoped_session)) – Connection or Session object SQL locking functions will be invoked on it

  • key (TypeVar(KeyTV)) – ID or name of the SQL locking function

  • contextual_timeout (float | int | None) –

    Timeout(seconds) for Context Managers.

    When called in a with statement, the new created lock object will pass it to timeout argument of BaseSadLock.acquire().

    Attention

    ONLY affects with statements.

    Example

    try:
        with create_sadlock(conn, k, contextual_timeout=5) as lck:
            # do something...
            pass
    except TimeoutError:
        # can not acquire after 5 seconds
        pass
    

    Note

    The default value of timeout is still None, when invoking acquire()

final acquire(block=True, timeout=None, *args, **kwargs)[source]

Acquire the lock in blocking or non-blocking mode.

The implementation (do_acquire()) should provide the following behavior:

  • When block is True (the default), the method blocks until the lock is in an unlocked state, then sets it to locked and returns True.

  • When block is False, the method call is non-blocking. If the lock is currently locked, it returns False; otherwise, it sets the lock to locked state and returns True.

  • When invoked with a positive floating-point value for timeout, it blocks for at most the specified number of seconds until the lock can be acquired.

  • Invocations with a negative timeout value are equivalent to a timeout of zero.

  • When timeout is None (the default), the timeout period is infinite. The timeout parameter has no effect when block is False and is thus ignored.

  • Returns True if the lock has been acquired or False if the timeout period has elapsed.

Return type:

bool

Parameters:
final close(*args, **kwargs)[source]

Same as release()

Except that the ValueError is NOT raised when invoked on an unlocked lock.

An invocation of this method is equivalent to:

if not some_lock.locked:
    some_lock.release()

This method maybe useful together with contextlib.closing(), when we need a with statement, but don’t want it to acquire at the beginning of the block.

Example

# ...

from contextlib import closing
from sqlalchemy_dlock import create_sadlock

# ...

with closing(create_sadlock(some_connection, some_key)) as lock:
    # will **NOT** acquire at the begin of with-block
    assert not lock.locked
    # ...
    # lock when need
    lock.acquire()
    assert lock.locked
    # ...

# `close` will be called at the end with-block
assert not lock.locked
property connection_or_session: ConnectionTV

Connection or Session object SQL locking functions will be invoked on it

It returns connection_or_session parameter of the class’s constructor.

abstractmethod do_acquire(block=True, timeout=None, *args, **kwargs)[source]
Return type:

bool

Parameters:
abstractmethod do_release(*args, **kwargs)[source]
property key: KeyTV

ID or name of the SQL locking function

It returns key parameter of the class’s constructor

property locked: bool

locked/unlocked state property

True if the lock is acquired, else False

final release(*args, **kwargs)[source]

Release the lock.

Since the class is thread-local, this method cannot be called from another thread or process, nor can it be called from another connection. (Although PostgreSQL’s shared advisory lock supports this).

The implementation (do_release()) should provide the following behavior:

  • Reset the lock to unlocked state and return when the lock is currently locked.

  • Allow exactly one of any other threads blocked waiting for the lock to become unlocked to proceed.

  • Raise a ValueError when invoked on an unlocked lock.

  • Not return a value.