icemac: @jimfulton What do you think about this PR are you okay with it?
jaraco: Bump
icemac: @jaraco This PR is approved and can be merged after signing the contributor agreement. Did you already sign it?
jaraco: Hi @icemac. I signed and submitted the contributor agreement on 2018-12-09. 
icemac: @jaraco Sorry for the inconvenience with the handling of your contributor agreement. In the process of merging the Zope foundation into the Plone foundation some requests to get access via a contributor agreement seem to have got dropped. There is now a new contributor agreement: https://www.zope.org/developer/becoming-a-committer.html May I ask you to resubmit your contributor agreement?
diff --git a/CHANGES.rst b/CHANGES.rst
index 64a5788..8d81187 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -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)
 ================
diff --git a/setup.py b/setup.py
index d2d97c1..0fe2746 100644
--- a/setup.py
+++ b/setup.py
@@ -44,7 +44,7 @@ setup(
     long_description=long_description,
     license = "ZPL 2.1",
     keywords = "lock",
-    url='http://www.python.org/pypi/zc.lockfile',
+    url='https://github.com/zopefoundation/zc.lockfile',
     packages = find_packages('src'),
     package_dir = {'': 'src'},
     namespace_packages = ['zc'],
diff --git a/src/zc/lockfile/__init__.py b/src/zc/lockfile/__init__.py
index a0ac2ff..b541fa2 100644
--- a/src/zc/lockfile/__init__.py
+++ b/src/zc/lockfile/__init__.py
@@ -17,6 +17,8 @@ import errno
 import logging
 logger = logging.getLogger("zc.lockfile")
 
+__metaclass__ = type
+
 class LockError(Exception):
     """Couldn't get a lock
     """
@@ -61,18 +63,18 @@ else:
     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:
@@ -86,15 +88,13 @@ class LockFile:
 
         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):
@@ -102,3 +102,24 @@ class LockFile:
             _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()

Allow reading/writing with acquired lock file
The current implementation of LockFile unconditionally writes some content and truncates the file upon acquiring the lock.

Some use cases, such as in pytest-dev/pytest-services#23, require that the client be able to read the content of the file and write more content to the file while holding the lock. This use-case isn't possible with zc.lockfile because the content is truncated.

I suggest instead the Lockfile class should allow clients to override this behavior.
diff --git a/src/zc/lockfile/tests.py b/src/zc/lockfile/tests.py
index e9fcbff..4c89053 100644
--- a/src/zc/lockfile/tests.py
+++ b/src/zc/lockfile/tests.py
@@ -179,6 +179,14 @@ class LockFileLogEntryTestCase(unittest.TestCase):
         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()

pip install -e .[test]
parse_log_pytest
requirements.txt
pytest zope.testing
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi @ file:///croot/certifi_1671487769961/work/certifi
exceptiongroup==1.2.2
importlib-metadata==6.7.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.2.0
pytest==7.4.4
tomli==2.0.1
typing_extensions==4.7.1
-e git+https://github.com/zopefoundation/zc.lockfile.git@c3ddaf120d390f3db2eb187b251968191d9ee2e1#egg=zc.lockfile
zipp==3.15.0
zope.testing==5.0.1

name: zc.lockfile
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - exceptiongroup==1.2.2
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - packaging==24.0
      - pluggy==1.2.0
      - pytest==7.4.4
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - zipp==3.15.0
      - zope-testing==5.0.1
prefix: /opt/conda/envs/zc.lockfile

src/zc/lockfile/tests.py::LockFileLogEntryTestCase::test_simple_lock
src/zc/lockfile/tests.py::LockFileLogEntryTestCase::test_log_formatting src/zc/lockfile/tests.py::LockFileLogEntryTestCase::test_unlock_and_lock_while_multiprocessing_process_running src/zc/lockfile/tests.py::test_suite
setup.py src/zc/lockfile/__init__.py CHANGES.rst
setup.py src/zc/lockfile/__init__.py CHANGES.rst