Mini Shell
"""Functions for acquiring a process lock"""
import socket
import hashlib
import contextlib
import time
from typing import Optional
import __main__ as main_obj
class LockError(Exception):
"""Raised when a rads.lock cannot be acquired"""
__module__ = 'rads'
@contextlib.contextmanager
def lock(name: Optional[str] = None):
"""Context manager to create an abstract UNIX socket which will behave as
a "lock". This can be used en lieu of /var/run file pids/locks to determine
if an instance of a process is already running and ensure the lock is not
left behind if the process terminates prematurely
Args:
name: unique name of the lock; defaults to filename of ``__main__``
Raises:
LockError: if the "lock" already existed
"""
if name is None:
# caveat: this will raise AttributeError
# if run in an interactive python shell
name = main_obj.__file__
name = name.encode('utf-8')
lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
try:
lock_socket.bind('\0%s' % hashlib.sha256(name).hexdigest())
except socket.error as exc:
raise LockError(name) from exc
try:
yield
finally:
lock_socket.close()
lock.__module__ = 'rads'
@contextlib.contextmanager
def wait_lock(
name: Optional[str] = None,
sleep: float = 0.2,
max_sleep: Optional[int] = None,
):
"""Context manager to wait until a rads.lock() can be acquired, and do so
Args:
name: unique name of the lock; defaults to filename of ``__main__``
sleep: secs before trying to acquire the lock again
max_sleep: max number of tries to acquire the lock
Raises:
LockError: if max_sleep is exceeded
"""
slept = 0.0
while True:
try:
with lock(name):
yield
return
except LockError:
time.sleep(sleep)
slept += sleep
if max_sleep is not None and slept >= max_sleep:
raise
wait_lock.__module__ = 'rads'
Zerion Mini Shell 1.0