Python Client
py-ldlm is an LDLM client library providing Python sync and async clients at https://github.com/imoore76/py-ldlm.
Installation
$ pip install py-ldlm
Basic Usage
Below are some basic usage examples.
See also
- LDLM Concepts
For a basic understanding of how locks function and the different locking methods available.
- LDLM Use Cases
For Python client examples of common use cases.
- API Reference
For a complete view of Python client functionality.
Client
import ldlm
client = ldlm.Client("ldlm-server:3144")
lock = client.lock("my-task")
try:
do_something()
finally:
lock.unlock()
# Lock will be unlocked when context manager exits
with client.lock_context("my-task"):
do_something()
Async Client
import ldlm
client = ldlm.AsyncClient("ldlm-server:3144")
lock = await client.lock("my-task")
try:
await do_something()
finally:
await lock.unlock()
# Lock will be unlocked when context manager exits
async with client.lock_context("my-task"):
await do_something()
TLS Configuration
Using TLS for LDLM client connections involves passing a ldlm.TLSConfig object to the client on instantiation.
import ldlm
client = ldlm.Client("ldlm-server:3144", tls=ldlm.TLSConfig(
ca_file="/etc/ldlm/certs/ca_cert.pem"
))
import ldlm
client = ldlm.Client("ldlm-server:3144", tls=ldlm.TLSConfig(
cert_file="/etc/ldlm/certs/client_cert.pem",
key_file="/etc/ldlm/certs/client_cert.pem",
ca_file="/etc/ldlm/certs/ca_cert.pem"
))
See also
Be sure to set up TLS in the server as described in Server TLS.
API Reference
- class ldlm.Client(address: str, password: str | None = None, tls: TLSConfig | None = None, retries: int = -1, retry_delay_seconds: int = 5, auto_renew_locks: bool = True, lock_timeout_seconds: int = 0)
Client class for interacting with the LDLM server.
- Parameters:
address (str) – The address of the server.
password (str, optional) – The password to use for authentication. Defaults to None.
tls (TLSConfig, optional) – TLS configuration. Leave None (default) to disable TLS.
retries (int, optional) – The number of retries to attempt. Defaults to -1 (infinite). Set to 0 to disable retries
retry_delay_seconds (int, optional) – The delay in seconds between retry attempts.
auto_renew_locks (bool, optional) – Automatically renew locks using a background thread or asyncio task
lock_timeout (int, optional) – The lock timeout to use for all lock operations
- lock(name: str, wait_timeout_seconds: int = 0, lock_timeout_seconds: int | None = None, size: int = 0) Lock
Acquire a lock with the given name.
If the client’s auto_renew_lock parameter was set to True (the default) or left unspecified, the lock will be automatically renewed at an appropriate interval using a background thread.
- Parameters:
name (str) – The name of the lock to acquire.
wait_timeout_seconds (int, optional) – The timeout in seconds to wait for the lock to be acquired. Defaults to 0 (wait indefinitely).
lock_timeout_seconds (int, optional) – The timeout in seconds after which the lock will be released unless it is renewed. Defaults to None (no timeout).
size (int, optional) – The size of the lock. Defaults to 0 which translates to unspecified. The server will use a size of 1 in this case.
- Returns:
The lock object.
- Return type:
- Raises:
ldlm.exceptions.LockSizeMismatchError – If the lock size does not match the size specified by a previous lock acquisition of this lock.
ldlm.exceptions.InvalidLockSizeError – If the lock size is invalid.
Examples
>>> from ldlm import Client >>> >>> client = Client("ldlm-server:3144") >>> >>> lock = client.lock( ... "my_lock", ... wait_timeout_seconds=10, ... lock_timeout_seconds=600, ... ) >>> if not lock: ... print("Could not acquire lock within 10 seconds") ... else: ... print("Doing work with lock") ... try: ... pass // do work ... finally: ... lock.unlock() ... print("Released lock") ... Doing work with lock Released lock >>> # This blocks until lock is obtained. >>> lock = client.lock( ... "my_lock", ... lock_timeout_seconds=600, ... ) >>> >>> print("Lock obtained") Lock obtained >>> # Do work with lock >>> try: ... pass // do work ... finally: ... lock.unlock() ... print("Released lock") >>> Released lock
- lock_context(name: str, wait_timeout_seconds: int = 0, lock_timeout_seconds: int | None = None, size: int = 0) Iterator[Lock]
A context manager that acquires a lock with the given name and unlocks the lock when the context is exited.
If the client’s auto_renew_lock parameter was set to True (the default) or left unspecified, the lock will be automatically renewed at an appropriate interval using a background thread.
- Parameters:
name (str) – The name of the lock to acquire.
wait_timeout_seconds (int, optional) – The timeout in seconds to wait for the lock to be acquired. Defaults to 0 (wait indefinitely).
lock_timeout_seconds (int, optional) – The timeout in seconds after which the lock will be released unless it is renewed. Defaults to None (no timeout).
size (int, optional) – The size of the lock. Defaults to 0 which translates to unspecified. The server will use a size of 1 in this case.
- Yields:
Lock – The lock object.
- Raises:
RuntimeError – If the lock cannot be released after being acquired.
ldlm.exceptions.LockSizeMismatchError – If the lock size does not match the size specified by a previous lock acquisition of this lock.
ldlm.exceptions.InvalidLockSizeError – If the lock size is invalid.
Examples
>>> from ldlm import Client >>> >>> client = Client("ldlm-server:3144") >>> >>> with client.lock_context( ... "my_lock", ... wait_timeout_seconds=10, ... lock_timeout_seconds=600, ... ) as lock: ... if not lock: ... print("Could not acquire lock") ... else: ... print("Doing work with lock...") ... pass ... print("Done") ... Doing work with lock... Done
- try_lock(name: str, lock_timeout_seconds: int | None = None, size: int = 0) Lock
Attempts to acquire a lock and immediately returns; whether the lock was acquired or not. Inspect the returned lock’s locked property or evaluate the lock as a boolean value to determine if it was acquired.
If the client’s auto_renew_lock parameter was set to True (the default) or left unspecified, the lock will be automatically renewed at an appropriate interval using a background thread.
- Parameters:
name (str) – The name of the lock to acquire.
lock_timeout_seconds (int, optional) – The timeout in seconds after which the lock will be released unless it is renewed. Defaults to None (no timeout).
size (int, optional) – The size of the lock. Defaults to 0 which translates to unspecified. The server will use a size of 1 in this case.
- Returns:
The lock object.
- Return type:
- Raises:
ldlm.exceptions.LockSizeMismatchError – If the lock size does not match the size specified by a previous lock acquisition of this lock.
ldlm.exceptions.InvalidLockSizeError – If the lock size is invalid.
Examples
>>> from ldlm import Client >>> >>> client = Client("ldlm-server:3144") >>> >>> lock = client.try_lock( ... "my_lock", ... lock_timeout_seconds=600, ... ) >>> if not lock: ... print("Could not acquire lock") ... else: ... print("Doing work with lock") ... try: ... pass // do work ... finally: ... lock.unlock() ... print("Released lock") ... Doing work with lock Released lock
- try_lock_context(name: str, lock_timeout_seconds: int | None = None, size: int = 0) Iterator[Lock]
A context manager that attempts to acquire a lock with the given name. You must inspect the returned lock’s locked property or evaluate it as a boolean value to determine if it was acquired. The lock is unlocked automatically when the context is exited.
If the client’s auto_renew_lock parameter was set to True (the default) or left unspecified, the lock will be automatically renewed at an appropriate interval using a background thread.
- Parameters:
name (str) – The name of the lock to acquire.
lock_timeout_seconds (int, optional) – The timeout in seconds after which the lock will be released unless it is renewed. Defaults to 0 (no timeout).
size (int, optional) – The size of the lock. Defaults to 0 which translates to unspecified. The server will use a size of 1 in this case.
- Yields:
Lock – The lock object.
- Raises:
RuntimeError – If the lock cannot be released after being acquired.
ldlm.exceptions.LockSizeMismatchError – If the lock size does not match the size specified by a previous lock acquisition of this lock.
ldlm.exceptions.InvalidLockSizeError – If the lock size is invalid.
Examples
>>> from ldlm import Client >>> >>> client = Client("ldlm-server:3144") >>> >>> with client.try_lock_context( ... "my_lock", ... lock_timeout_seconds=600, ... ) as lock: ... if not lock: ... print("Could not acquire lock") ... else: ... print("Doing work with lock...") ... pass ... print("Done") ... Doing work with lock... Done
- renew(name: str, key: str, lock_timeout_seconds: int) Lock
Renews a lock. It is much more concise to run this method on the
ldlm.Lockobject returned by this client’s lock methods.- Parameters:
name (str) – The name of the lock to renew.
key (str) – The key associated with the lock to renew.
lock_timeout_seconds (int) – The timeout in seconds for acquiring the lock.
- Returns:
A lock object.
- Return type:
- Raises:
ldlm.exceptions.LockDoesNotExistOrInvalidKeyError – If the lock does not exist or the lock key is invalid.
- unlock(name: str, key: str) None
Unlock the lock with the specified name and key. It is much more concise to run this method on the
ldlm.Lockobject returned by this client’s lock methods.- Parameters:
name (str) – The name of the lock to unlock.
key (str) – The key associated with the lock to unlock.
- Raises:
RuntimeError – If the lock cannot be unlocked.
ldlm.exceptions.LockDoesNotExistError – If the lock does not exist.
ldlm.exceptions.NotLockedError – If the lock is already unlocked.
- Returns:
None
- close() None
Closes the LDLM gRPC channel.
This method is used to close the LDLM gRPC channel and indicate that the client is no longer active. It is typically called when the client is no longer needed or when the program is exiting.
- Returns:
None
- class ldlm.Lock(client: Client, lock: LockResponse)
A lock returned by LDLM Client lock methods.
- Parameters:
client (Client) – The client object.
lock (pb.LockResponse) – An LDLM lock response object.
- name: str
name of the lock
- key: str
key associated with the lock
- locked: bool
whether the lock is locked or not
- unlock() None
Unlocks the lock.
- Returns:
None
- Raises:
RuntimeError – If the lock is not locked
- renew(lock_timeout_seconds: int) None
Renews the lock.
- Parameters:
lock_timeout_seconds (int) – The timeout in seconds after which the lock will expire.
- Returns:
None
- Raises:
RuntimeError – If the lock is not locked
- class ldlm.AsyncClient(address: str, password: str | None = None, tls: TLSConfig | None = None, retries: int = -1, retry_delay_seconds: int = 5, auto_renew_locks: bool = True, lock_timeout_seconds: int = 0)
asyncio client class for interacting with the LDLM server.
- Parameters:
address (str) – The address of the server.
password (str, optional) – The password to use for authentication. Defaults to None.
tls (TLSConfig, optional) – TLS configuration. Leave None (default) to disable TLS.
retries (int, optional) – The number of retries to attempt. Defaults to -1 (infinite). Set to 0 to disable retries
retry_delay_seconds (int, optional) – The delay in seconds between retry attempts.
auto_renew_locks (bool, optional) – Automatically renew locks using a background thread or asyncio task
lock_timeout (int, optional) – The lock timeout to use for all lock operations
- async lock(name: str, wait_timeout_seconds: int = 0, lock_timeout_seconds: int | None = None, size: int = 0) AsyncLock
Acquires a lock with the given name.
If the client’s auto_renew_lock parameter was set to True (the default) or left unspecified, the lock will be automatically renewed at an appropriate interval using a background asyncio task.
- Parameters:
name (str) – The name of the lock to acquire.
wait_timeout_seconds (int, optional) – The timeout in seconds to wait for the lock to be acquired. Defaults to 0 (wait indefinitely).
lock_timeout_seconds (int, optional) – The timeout in seconds after which the lock will be released unless it is renewed. Defaults to None (no timeout).
size (int, optional) – The size of the lock. Defaults to 0 which translates to unspecified. The server will use a size of 1 in this case.
- Returns:
A lock object.
- Return type:
Examples
>>> import asyncio >>> from ldlm import AsyncClient >>> >>> async def test_lock(): ... client = AsyncClient("ldlm-server:3144") ... lock = await client.lock( ... "test_lock", ... wait_timeout_seconds=10, ... lock_timeout_seconds=600, ... ) ... if not lock: ... print("Could not acquire lock within 10 seconds") ... return ... print("Doing work with lock") ... try: ... pass # do some work with the lock ... finally: ... await lock.unlock() ... print("Released lock") ... >>> asyncio.run(test_lock()) Doing work with lock Released lock
- lock_context(name: str, wait_timeout_seconds: int = 0, lock_timeout_seconds: int | None = None, size: int = 0) AsyncIterator[AsyncLock]
A context manager that acquires a lock and unlocks it when the context is exited.
If the client’s auto_renew_lock parameter was set to True (the default) or left unspecified, the lock will be automatically renewed at an appropriate interval using a background asyncio task.
- Parameters:
name (str) – The name of the lock to acquire.
wait_timeout_seconds (int, optional) – The timeout in seconds to wait for the lock to be acquired. Defaults to 0 (wait indefinitely).
lock_timeout_seconds (int, optional) – The timeout in seconds after which the lock will be released unless it is renewed. Defaults to 0 (no timeout).
size (int, optional) – The size of the lock. Defaults to 0 which translates to unspecified. The server will use a size of 1 in this case.
- Yields:
AsyncLock – A lock object.
- Raises:
RuntimeError – If the lock cannot be released after being acquired.
Examples
>>> import asyncio >>> from ldlm import AsyncClient >>> >>> async def test_lock_context(): ... client = AsyncClient("ldlm-server:3144") ... ... async with client.lock_context( ... "my_lock", ... wait_timeout_seconds=10, ... lock_timeout_seconds=600, ... ) as lock: ... if not lock: ... print("Could not acquire lock within 10 seconds") ... print("Doing work with lock") ... >>> asyncio.run(test_lock_context()) Doing work with lock
- async try_lock(name: str, lock_timeout_seconds: int | None = None, size: int = 0) AsyncLock
Attempts to acquire a lock and immediately returns; whether the lock was acquired or not. You must inspect the returned lock’s locked property or evaluate it as a boolean value to determine if it was acquired.
If the client’s auto_renew_lock parameter was set to True (the default) or left unspecified, the lock will be automatically renewed at an appropriate interval using a background asyncio task.
- Parameters:
name (str) – The name of the lock to acquire.
lock_timeout_seconds (int, optional) – The timeout in seconds after which the lock will be released unless it is renewed. Defaults to None (no timeout).
size (int, optional) – The size of the lock. Defaults to 0 which translates to unspecified. The server will use a size of 1 in this case.
- Yields:
AsyncLock – A lock object.
- Raises:
RuntimeError – If the lock cannot be released after being acquired.
Examples
>>> async def test_try_lock(): ... client = AsyncClient("ldlm-server:3144") ... ... lock = await client.try_lock( ... "my_lock", ... lock_timeout_seconds=600, ... ) ... if not lock: ... print("Could not acquire lock") ... return ... print("Doing work with lock") ... try: ... pass # do some work with the lock ... finally: ... await lock.unlock() ... print("Released lock") ... >>> asyncio.run(test_try_lock()) Doing work with lock Released lock
- try_lock_context(name: str, lock_timeout_seconds: int | None = None, size: int = 0) AsyncIterator[AsyncLock]
A context manager that attempts to acquire a lock with the given name. You must inspect the returned lock’s locked property or evaluate it as a boolean value to determine if it was acquired. If locked, the lock will be released when the context is exited.
If the client’s auto_renew_lock parameter was set to True (the default) or left unspecified, the lock will be automatically renewed at an appropriate interval using a background asyncio task.
- Parameters:
name (str) – The name of the lock to acquire.
lock_timeout_seconds (int, optional) – The timeout in seconds after which the lock will be released unless it is renewed. Defaults to None (no timeout).
size (int, optional) – The size of the lock. Defaults to 0 which translates to unspecified. The server will use a size of 1 in this case.
- Yields:
AsyncLock – A lock object.
- Raises:
RuntimeError – If the lock cannot be released after being acquired.
Examples
>>> async def test_try_lock_context(): ... client = AsyncClient("ldlm-server:3144") ... ... async with client.try_lock_context( ... "my_lock", ... lock_timeout_seconds=600, ... ) as lock: ... if not lock: ... print("Could not acquire lock") ... print("Doing work with lock") ... >>> asyncio.run(test_try_lock_context()) Doing work with lock
- async unlock(name: str, key: str) None
Unlock the specified lock. It is much more concise to run this method on the
ldlm.AsyncLockobject returned by this client’s lock methods.- Parameters:
name (str) – The name of the lock to unlock.
key (str) – The key associated with the lock to unlock.
- Raises:
RuntimeError – If the lock cannot be unlocked.
- async renew(name: str, key: str, lock_timeout_seconds: int) AsyncLock
Renews a lock. It is much more concise to run this method on the
ldlm.AsyncLockobject returned by this client’s lock methods.- Parameters:
name (str) – The name of the lock to renew.
key (str) – The key associated with the lock to renew.
lock_timeout_seconds (int) – The timeout in seconds for acquiring the lock.
- Returns:
A lock object.
- Return type:
- async close() None
Closes the LDLM gRPC channel.
This method is used to close the LDLM gRPC channel and indicate that the client is no longer active. It is typically called when the client is no longer needed or when the program is exiting.
- Returns:
None
- async aclose() None
Awaits self.close(). For compatibility with contextlib.aclosing().
- Returns:
None
- class ldlm.AsyncLock(client: AsyncClient, lock: LockResponse)
A lock returned by LDLM AsyncClient lock methods.
- Parameters:
client (Client) – The client object.
lock (pb.LockResponse) – An LDLM lock response object.
- name: str
name of the lock
- key: str
key associated with the lock
- locked: bool
whether the lock is locked or not
- async unlock() None
Unlocks the lock.
- Returns:
None
- Raises:
RuntimeError – If the lock is not locked
- async renew(lock_timeout_seconds: int) None
Renews the lock.
- Parameters:
lock_timeout_seconds (int) – The timeout in seconds after which the lock will expire.
- Returns:
None
- Raises:
RuntimeError – If the lock is not locked
TLS Config
- class ldlm.TLSConfig(cert_file: str | None = None, key_file: str | None = None, ca_file: str | None = None)
TLS configuration dataclass for LDLM client. Pass an instance of this class as the tls parameter to an LDLM client constructor.
- cert_file: str | None = None
Path to the client certificate file
- key_file: str | None = None
Path to the client key file
- ca_file: str | None = None
Path to the CA certificate file
Exceptions
Exception classes for the LDLM service.
- exception ldlm.exceptions.LDLMError(message)
Generic LDLM error.
- exception ldlm.exceptions.LockDoesNotExistError(message)
Lock does not exist error. This can occur when attempting to unlock or renew a lock that does not exist.
- exception ldlm.exceptions.InvalidLockKeyError(message)
Invalid lock key error. The key specified in the request is not valid.
- exception ldlm.exceptions.LockWaitTimeoutError(message)
Lock wait timeout error. The lock could not be acquired in wait_timeout_seconds seconds.
- exception ldlm.exceptions.NotLockedError(message)
Lock is not locked error. This can occur when attempting to renew or unlock a lock that is not locked.
- exception ldlm.exceptions.LockDoesNotExistOrInvalidKeyError(message)
Lock does not exist or invalid key error. This can occur when renewing a lock using an invalid name or key.
- exception ldlm.exceptions.LockSizeMismatchError(message)
The size of the lock in the LDLM server does not match the size specified. A previous lock request was made with a different size.
- exception ldlm.exceptions.InvalidLockSizeError(message)
The specified size in the lock request is not a valid size (must be > 0).