Skip to content

Commit

Permalink
Implement custom acquistion behavior. (#16)
Browse files Browse the repository at this point in the history
* Extract SimpleLockFile which writes nothing to the locked file. Fixes #15.
  • Loading branch information
jaraco authored and Michael Howitz committed Aug 7, 2019
1 parent 9b8b871 commit 43c5f1d
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 10 deletions.
9 changes: 7 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
Change History
***************

1.5 (unreleased)
2.0 (unreleased)
================

- Nothing changed yet.
- #15: Extracted new ``SimpleLockFile`` that removes implicit behavior
writing to the lock file, and instead allows a subclass to define
that behavior.

- ``SimpleLockFile`` and thus ``LockFile`` are now new-style classes.
Any clients relying on ``LockFile`` being an old-style class will
need to be adapted.

1.4 (2018-11-12)
================
Expand Down
37 changes: 29 additions & 8 deletions src/zc/lockfile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import logging
logger = logging.getLogger("zc.lockfile")

__metaclass__ = type

class LockError(Exception):
"""Couldn't get a lock
"""
Expand Down Expand Up @@ -61,18 +63,18 @@ def _lock_file(file):
def _unlock_file(file):
fcntl.flock(file.fileno(), fcntl.LOCK_UN)

class LazyHostName(object):
class LazyHostName:
"""Avoid importing socket and calling gethostname() unnecessarily"""
def __str__(self):
import socket
return socket.gethostname()


class LockFile:
class SimpleLockFile:

_fp = None

def __init__(self, path, content_template='{pid}'):
def __init__(self, path):
self._path = path
try:
# Try to open for writing without truncation:
Expand All @@ -86,19 +88,38 @@ def __init__(self, path, content_template='{pid}'):

try:
_lock_file(fp)
self._fp = fp
except:
fp.close()
raise

# We got the lock, record info in the file.
self._fp = fp
fp.write(" %s\n" % content_template.format(pid=os.getpid(),
hostname=LazyHostName()))
fp.truncate()
# Lock acquired
self._on_lock()
fp.flush()

def close(self):
if self._fp is not None:
_unlock_file(self._fp)
self._fp.close()
self._fp = None

def _on_lock(self):
"""
Allow subclasses to supply behavior to occur following
lock acquisition.
"""


class LockFile(SimpleLockFile):

def __init__(self, path, content_template='{pid}'):
self._content_template = content_template
super(LockFile, self).__init__(path)

def _on_lock(self):
content = self._content_template.format(
pid=os.getpid(),
hostname=LazyHostName(),
)
self._fp.write(" %s\n" % content)
self._fp.truncate()
8 changes: 8 additions & 0 deletions src/zc/lockfile/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ def test_unlock_and_lock_while_multiprocessing_process_running(self):
lock.close()
p.join()

def test_simple_lock(self):
assert isinstance(zc.lockfile.SimpleLockFile, type)
lock = zc.lockfile.SimpleLockFile('s')
with self.assertRaises(zc.lockfile.LockError):
zc.lockfile.SimpleLockFile('s')
lock.close()
zc.lockfile.SimpleLockFile('s').close()


def test_suite():
suite = unittest.TestSuite()
Expand Down

0 comments on commit 43c5f1d

Please sign in to comment.