__repr__() should not refresh the lock

Problem

__repr__ evaluates self.is_locked

def __repr__(self):
        return '<%s %s [%s: %s] pid=%s at %#xx>' % (
            self.__class__.__name__,
            self._lockfile,
            ('locked' if self.is_locked else 'unlocked'),
            self._lifetime, os.getpid(), id(self))

self.is_locked resets the lifetime of the lock.

 @property
    def is_locked(self):
        """True if we own the lock, False if we do not.

        Checking the status of the lock resets the lock's lifetime, which
        helps avoid race conditions during the lock status test.
        """
        # Discourage breaking the lock for a while.
        try:
            self._touch()
        except PermissionError:
            # We can't touch the file because we're not the owner.  I don't see
            # how we can own the lock if we're not the owner.
            log.error('No permission to refresh the log')
            return False
        # XXX Can the link count ever be > 2?
        if self._linkcount != 2:
            return False
        return self._read() == self._claimfile

A way this may become a problem is when an unknowing user uses this in logging or prints it out.

>>> import logging
>>> logging.basicConfig()
>>> logging.getLogger().setLevel(logging.DEBUG)
>>> import datetime
>>> from flufl.lock import Lock
>>> a = Lock('b.txt', lifetime=datetime.timedelta(seconds=10))
DEBUG:flufl.lock:__del__: b.txt
DEBUG:flufl.lock:finalize: b.txt
DEBUG:flufl.lock:unlocked: b.txt
>>> a.lock()
DEBUG:flufl.lock:laying claim: b.txt
DEBUG:flufl.lock:got the lock: b.txt
>>> time.sleep(10)
>>> f"{a}"
'<Lock b.txt [locked: 0:00:10] pid=31296 at 0x10fce1790x>'
>>> b.lock(datetime.timedelta(seconds=1))
DEBUG:flufl.lock:laying claim: b.txt
DEBUG:flufl.lock:waiting for claim: b.txt
ERROR:flufl.lock:timed out
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/molee/lipy-deployment-runner_trunk/build/lipy-deployment-runner/environments/development-venv/lib/python3.7/site-packages/flufl/lock/_lockfile.py", line 259, in lock
    raise TimeOutError('Could not acquire the lock')
flufl.lock._lockfile.TimeOutError: Could not acquire the lock

Solution

I think the solution can be 2 parts:

  1. Add support for __str__ that doesn't use is_locked or remove the refresh functionality from is_locked and put it in refresh instead. I think the latter might be more bug free.
  2. If a process owns the lock but the lock expired, the is_locked will still print True. We might want to add checks the lock hasn't expired before printing True.

Thanks!

Edited by Barry Warsaw