This program provides a way for different processes or machines to coordinate access to shared files by asking a central locker service to grant or release locks, and it also lets those processes store or retrieve small pieces of data associated with each locked file.
The file defines a single public class named Locker that acts as a client for a remote lock‑manager service.
When a Locker object is created (__init__), it stores configuration such as the target file name, connection details (host = 127.0.0.1 by default, port = 37373), retry limits, timeouts and an optional identifier. If no identifier is supplied the object generates one with GetID, which seeds Python's random generator with the current time and then asks secrets.token_urlsafe for a random string of length between 37 and 72 characters.
The class builds two lookup tables for a custom binary‑to‑text encoding: ENCODE_TABLE maps each byte value (0‑255) to two characters taken from ALPHABET, while DECODE_TABLE does the reverse mapping. The default encoder (dlmEncode) first converts its argument to UTF‑8 bytes (serialising dicts to JSON first) and then substitutes each byte using ENCODE_TABLE. The default decoder (dlmDecode) reverses this process by reading the encoded string two characters at a time and looking up the original byte in DECODE_TABLE.
Network communication is handled by Talker. It takes a Python object (msg), JSON‑serialises it if needed,
encodes the resulting string with dlmEncode, appends a newline,
opens a TCP connection to (self.host,self.port) using socket.create_connection with the configured timeout,
writes the encoded payload via a line‑buffered file object made from the socket (makefile), flushes,
reads one line back with readline (which respects the socket timeout),
closes both the file object and the socket,
decodes the received line with dlmDecode,
optionally lower‑cases it when casefold=True,
and returns either the decoded bytes/string or None on any socket‐related exception while storing
the exception message in self.Error.
Higher level actions are performed through two families of helper methods:
- Retry - builds a minimal JSON payload containing
{ID,self.ID,'FileName':self.filename,'Action':action,'Expire':str(expire)}plus optional Name/Identity fields if they were supplied. It serialises this dict to JSON (outbuf) and repeatedly callsTalker(outbuf)until either:- a non‑empty response is received that can be parsed as JSON,
from which it extracts either top‑level
"status"/"Status"key (depending on casefold) - if that value matches one of'badpayload','locked','unlocked','notowner','notfound','version','no'stored inself.ulResp, looping stops; - or nothing is returned - after exceeding
self.retryLimit attempts it returns
'failure'. Between attempts it sleeps for self.retrysleep seconds.
- a non‑empty response is received that can be parsed as JSON,
from which it extracts either top‑level
- RetryData - works like Retry but includes an extra
'DataStore':datafield in its payload. It disables case folding when talking to Talker, retries on empty responses exactly as Retry does, stops when any non‑empty buffer is received, and finally returns that raw buffer unchanged.
Public convenience methods wrap these helpers:
- Lock(expire) → calls Retry('Lock',expire).
- Unlock() → calls Retry('Unlock',0).
- IsLocked(expire) → builds same payload as Lock but talks once; parses any returned JSON for its status field ('status' key) returning that value or None/'failure'.
- Put(expire,data) → encodes data via self.dlmEncode then calls RetryData('Put',expire,self.dlmEncode(data)).
- Get() → calls RetryData('Get',0,null); tries json.loads on result; if successful extracts 'DataStore', runs self.decoder on it, converts resulting bytes back to UTF‑8 text if needed, returning whole dict.
- Erase() → calls RetryData('Erase',0,null).
- Version() → same pattern as Get but asks action 'Version';
parses returned JSON for an 'ID' field;
concatenates that server ID literal colon followed by
self.VERSION ('0.0.0.1.530') into string like
<serverID>:<localVersion>. - IsDLM() → invokes Version(); returns True iff substring
'JackrabbitDLM'appears anywhere in that version string.
All methods return whatever their underlying helper produced:
typically strings such as 'locked', 'unlocked', 'failure',
or dictionaries containing server-supplied fields including possibly
a decoded DataStore blob when dealing with Get/Put operations.
No attempt is made here to fix logic errors nor comment on design quality-only what each part actually does when executed has been described above.*