././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1677569407.1854417 zc.lockfile-3.0.post1/0000755000100100000240000000000014377326577013314 5ustar00macstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/CHANGES.rst0000644000100100000240000000441514377326576015121 0ustar00macstaffChange History *************** 3.0.post1 (2023-02-28) ====================== - Add ``python_requires`` to ``setup.py`` to prevent installing on not supported old Python versions. 3.0 (2023-02-23) ================ - Add support for Python 3.9, 3.10, 3.11. - Drop support for Python 2.7, 3.5, 3.6. - Drop support for deprecated ``python setup.py test``. 2.0 (2019-08-08) ================ - Extracted new ``SimpleLockFile`` that removes implicit behavior writing to the lock file, and instead allows a subclass to define that behavior. (`#15 `_) - ``SimpleLockFile`` and thus ``LockFile`` are now new-style classes. Any clients relying on ``LockFile`` being an old-style class will need to be adapted. - Drop support for Python 3.4. - Add support for Python 3.8b3. 1.4 (2018-11-12) ================ - Claim support for Python 3.6 and 3.7. - Drop Python 2.6 and 3.3. 1.3.0 (2018-04-23) ================== - Stop logging failure to acquire locks. Clients can do that if they wish. - Claim support for Python 3.4 and 3.5. - Drop Python 3.2 support because pip no longer supports it. 1.2.1 (2016-06-19) ================== - Fixed: unlocking and locking didn't work when a multiprocessing process was running (and presumably other conditions). 1.2.0 (2016-06-09) ================== - Added the ability to include the hostname in the lock file content. - Code and ReST markup cosmetics. [alecghica] 1.1.0 (2013-02-12) ================== - Added Trove classifiers and made setup.py zest.releaser friendly. - Added Python 3.2, 3.3 and PyPy 1.9 support. - Removed Python 2.4 and Python 2.5 support. 1.0.2 (2012-12-02) ================== - Fixed: the fix included in 1.0.1 caused multiple pids to be written to the lock file 1.0.1 (2012-11-30) ================== - Fixed: when there was lock contention, the pid in the lock file was lost. Thanks to Daniel Moisset reporting the problem and providing a fix with tests. - Added test extra to declare test dependency on ``zope.testing``. - Using Python's ``doctest`` module instead of depreacted ``zope.testing.doctest``. 1.0.0 (2008-10-18) ================== - Fixed a small bug in error logging. 1.0.0b1 (2007-07-18) ==================== - Initial release ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/CONTRIBUTING.md0000644000100100000240000000144414377326576015547 0ustar00macstaff # Contributing to zopefoundation projects The projects under the zopefoundation GitHub organization are open source and welcome contributions in different forms: * bug reports * code improvements and bug fixes * documentation improvements * pull request reviews For any changes in the repository besides trivial typo fixes you are required to sign the contributor agreement. See https://www.zope.dev/developer/becoming-a-committer.html for details. Please visit our [Developer Guidelines](https://www.zope.dev/developer/guidelines.html) if you'd like to contribute code changes and our [guidelines for reporting bugs](https://www.zope.dev/developer/reporting-bugs.html) if you want to file a bug report. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/COPYRIGHT.txt0000644000100100000240000000004014377326576015416 0ustar00macstaffZope Foundation and Contributors././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/LICENSE.txt0000644000100100000240000000402614377326576015140 0ustar00macstaffZope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/MANIFEST.in0000644000100100000240000000034014377326576015046 0ustar00macstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python include *.md include *.rst include *.txt include buildout.cfg include tox.ini recursive-include src *.py recursive-include src *.txt ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1677569407.185636 zc.lockfile-3.0.post1/PKG-INFO0000644000100100000240000001375314377326577014422 0ustar00macstaffMetadata-Version: 2.1 Name: zc.lockfile Version: 3.0.post1 Summary: Basic inter-process locks Home-page: https://github.com/zopefoundation/zc.lockfile Author: Zope Foundation Author-email: zope-dev@zope.dev License: ZPL 2.1 Keywords: lock Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Natural Language :: English Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development Requires-Python: >=3.7 Provides-Extra: test License-File: LICENSE.txt ************************* Basic inter-process locks ************************* The zc.lockfile package provides a basic portable implementation of interprocess locks using lock files. The purpose if not specifically to lock files, but to simply provide locks with an implementation based on file-locking primitives. Of course, these locks could be used to mediate access to *other* files. For example, the ZODB file storage implementation uses file locks to mediate access to file-storage database files. The database files and lock file files are separate files. .. contents:: Detailed Documentation ********************** Lock file support ================= The ZODB lock_file module provides support for creating file system locks. These are locks that are implemented with lock files and OS-provided locking facilities. To create a lock, instantiate a LockFile object with a file name: >>> import zc.lockfile >>> lock = zc.lockfile.LockFile('lock') If we try to lock the same name, we'll get a lock error: >>> import zope.testing.loggingsupport >>> handler = zope.testing.loggingsupport.InstalledHandler('zc.lockfile') >>> try: ... zc.lockfile.LockFile('lock') ... except zc.lockfile.LockError: ... print("Can't lock file") Can't lock file .. We don't log failure to acquire. >>> for record in handler.records: # doctest: +ELLIPSIS ... print(record.levelname+' '+record.getMessage()) To release the lock, use it's close method: >>> lock.close() The lock file is not removed. It is left behind: >>> import os >>> os.path.exists('lock') True Of course, now that we've released the lock, we can create it again: >>> lock = zc.lockfile.LockFile('lock') >>> lock.close() .. Cleanup >>> import os >>> os.remove('lock') Hostname in lock file ===================== In a container environment (e.g. Docker), the PID is typically always identical even if multiple containers are running under the same operating system instance. Clearly, inspecting lock files doesn't then help much in debugging. To identify the container which created the lock file, we need information about the container in the lock file. Since Docker uses the container identifier or name as the hostname, this information can be stored in the lock file in addition to or instead of the PID. Use the ``content_template`` keyword argument to ``LockFile`` to specify a custom lock file content format: >>> lock = zc.lockfile.LockFile('lock', content_template='{pid};{hostname}') >>> lock.close() If you now inspected the lock file, you would see e.g.: $ cat lock 123;myhostname Change History *************** 3.0.post1 (2023-02-28) ====================== - Add ``python_requires`` to ``setup.py`` to prevent installing on not supported old Python versions. 3.0 (2023-02-23) ================ - Add support for Python 3.9, 3.10, 3.11. - Drop support for Python 2.7, 3.5, 3.6. - Drop support for deprecated ``python setup.py test``. 2.0 (2019-08-08) ================ - Extracted new ``SimpleLockFile`` that removes implicit behavior writing to the lock file, and instead allows a subclass to define that behavior. (`#15 `_) - ``SimpleLockFile`` and thus ``LockFile`` are now new-style classes. Any clients relying on ``LockFile`` being an old-style class will need to be adapted. - Drop support for Python 3.4. - Add support for Python 3.8b3. 1.4 (2018-11-12) ================ - Claim support for Python 3.6 and 3.7. - Drop Python 2.6 and 3.3. 1.3.0 (2018-04-23) ================== - Stop logging failure to acquire locks. Clients can do that if they wish. - Claim support for Python 3.4 and 3.5. - Drop Python 3.2 support because pip no longer supports it. 1.2.1 (2016-06-19) ================== - Fixed: unlocking and locking didn't work when a multiprocessing process was running (and presumably other conditions). 1.2.0 (2016-06-09) ================== - Added the ability to include the hostname in the lock file content. - Code and ReST markup cosmetics. [alecghica] 1.1.0 (2013-02-12) ================== - Added Trove classifiers and made setup.py zest.releaser friendly. - Added Python 3.2, 3.3 and PyPy 1.9 support. - Removed Python 2.4 and Python 2.5 support. 1.0.2 (2012-12-02) ================== - Fixed: the fix included in 1.0.1 caused multiple pids to be written to the lock file 1.0.1 (2012-11-30) ================== - Fixed: when there was lock contention, the pid in the lock file was lost. Thanks to Daniel Moisset reporting the problem and providing a fix with tests. - Added test extra to declare test dependency on ``zope.testing``. - Using Python's ``doctest`` module instead of depreacted ``zope.testing.doctest``. 1.0.0 (2008-10-18) ================== - Fixed a small bug in error logging. 1.0.0b1 (2007-07-18) ==================== - Initial release ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/README.rst0000644000100100000240000000110714377326576015001 0ustar00macstaff************************* Basic inter-process locks ************************* The zc.lockfile package provides a basic portable implementation of interprocess locks using lock files. The purpose if not specifically to lock files, but to simply provide locks with an implementation based on file-locking primitives. Of course, these locks could be used to mediate access to *other* files. For example, the ZODB file storage implementation uses file locks to mediate access to file-storage database files. The database files and lock file files are separate files. .. contents:: ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1677569407.186284 zc.lockfile-3.0.post1/setup.cfg0000644000100100000240000000064714377326577015144 0ustar00macstaff[bdist_wheel] universal = 0 [flake8] doctests = 1 [check-manifest] ignore = .editorconfig .meta.toml [isort] force_single_line = True combine_as_imports = True sections = FUTURE,STDLIB,THIRDPARTY,ZOPE,FIRSTPARTY,LOCALFOLDER known_third_party = six, docutils, pkg_resources, pytz known_zope = known_first_party = default_section = ZOPE line_length = 79 lines_after_imports = 2 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/setup.py0000644000100100000240000000465014377326576015032 0ustar00macstaff############################################################################## # # Copyright (c) Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import os from setuptools import find_packages from setuptools import setup version = '3.0.post1' def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() long_description = ( read('README.rst') + '\n' + 'Detailed Documentation\n' '**********************\n' + '\n' + read('src', 'zc', 'lockfile', 'README.txt') + '\n' + read('CHANGES.rst') ) setup( name='zc.lockfile', version=version, author="Zope Foundation", author_email="zope-dev@zope.dev", description="Basic inter-process locks", long_description=long_description, license="ZPL 2.1", keywords="lock", url='https://github.com/zopefoundation/zc.lockfile', packages=find_packages('src'), package_dir={'': 'src'}, namespace_packages=['zc'], python_requires='>=3.7', install_requires='setuptools', extras_require=dict( test=[ 'zope.testing', ]), classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Natural Language :: English', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development', ], include_package_data=True, zip_safe=False, ) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1677569407.178024 zc.lockfile-3.0.post1/src/0000755000100100000240000000000014377326577014103 5ustar00macstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1677569407.1814818 zc.lockfile-3.0.post1/src/zc/0000755000100100000240000000000014377326577014517 5ustar00macstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/src/zc/__init__.py0000644000100100000240000000007014377326576016624 0ustar00macstaff__import__('pkg_resources').declare_namespace(__name__) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1677569407.1850445 zc.lockfile-3.0.post1/src/zc/lockfile/0000755000100100000240000000000014377326577016307 5ustar00macstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/src/zc/lockfile/README.txt0000644000100100000240000000400014377326576017776 0ustar00macstaffLock file support ================= The ZODB lock_file module provides support for creating file system locks. These are locks that are implemented with lock files and OS-provided locking facilities. To create a lock, instantiate a LockFile object with a file name: >>> import zc.lockfile >>> lock = zc.lockfile.LockFile('lock') If we try to lock the same name, we'll get a lock error: >>> import zope.testing.loggingsupport >>> handler = zope.testing.loggingsupport.InstalledHandler('zc.lockfile') >>> try: ... zc.lockfile.LockFile('lock') ... except zc.lockfile.LockError: ... print("Can't lock file") Can't lock file .. We don't log failure to acquire. >>> for record in handler.records: # doctest: +ELLIPSIS ... print(record.levelname+' '+record.getMessage()) To release the lock, use it's close method: >>> lock.close() The lock file is not removed. It is left behind: >>> import os >>> os.path.exists('lock') True Of course, now that we've released the lock, we can create it again: >>> lock = zc.lockfile.LockFile('lock') >>> lock.close() .. Cleanup >>> import os >>> os.remove('lock') Hostname in lock file ===================== In a container environment (e.g. Docker), the PID is typically always identical even if multiple containers are running under the same operating system instance. Clearly, inspecting lock files doesn't then help much in debugging. To identify the container which created the lock file, we need information about the container in the lock file. Since Docker uses the container identifier or name as the hostname, this information can be stored in the lock file in addition to or instead of the PID. Use the ``content_template`` keyword argument to ``LockFile`` to specify a custom lock file content format: >>> lock = zc.lockfile.LockFile('lock', content_template='{pid};{hostname}') >>> lock.close() If you now inspected the lock file, you would see e.g.: $ cat lock 123;myhostname ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/src/zc/lockfile/__init__.py0000644000100100000240000000664414377326576020431 0ustar00macstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## import logging import os logger = logging.getLogger("zc.lockfile") class LockError(Exception): """Couldn't get a lock """ try: import fcntl except ImportError: try: import msvcrt except ImportError: def _lock_file(file): raise TypeError('No file-locking support on this platform') def _unlock_file(file): raise TypeError('No file-locking support on this platform') else: # Windows def _lock_file(file): # Lock just the first byte try: msvcrt.locking(file.fileno(), msvcrt.LK_NBLCK, 1) except OSError: raise LockError("Couldn't lock %r" % file.name) def _unlock_file(file): try: file.seek(0) msvcrt.locking(file.fileno(), msvcrt.LK_UNLCK, 1) except OSError: raise LockError("Couldn't unlock %r" % file.name) else: # Unix _flags = fcntl.LOCK_EX | fcntl.LOCK_NB def _lock_file(file): try: fcntl.flock(file.fileno(), _flags) except OSError: raise LockError("Couldn't lock %r" % file.name) def _unlock_file(file): fcntl.flock(file.fileno(), fcntl.LOCK_UN) class LazyHostName: """Avoid importing socket and calling gethostname() unnecessarily""" def __str__(self): import socket return socket.gethostname() class SimpleLockFile: _fp = None def __init__(self, path): self._path = path try: # Try to open for writing without truncation: fp = open(path, 'r+') except OSError: # If the file doesn't exist, we'll get an IO error, try a+ # Note that there may be a race here. Multiple processes # could fail on the r+ open and open the file a+, but only # one will get the the lock and write a pid. fp = open(path, 'a+') try: _lock_file(fp) self._fp = fp except BaseException: fp.close() raise # 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().__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() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/src/zc/lockfile/tests.py0000644000100100000240000001272314377326576020027 0ustar00macstaff############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import doctest import os import tempfile import threading import time import unittest from unittest.mock import Mock from unittest.mock import patch from zope.testing import setupstack import zc.lockfile def inc(): while True: try: lock = zc.lockfile.LockFile('f.lock') except zc.lockfile.LockError: continue else: break f = open('f', 'r+b') v = int(f.readline().strip()) time.sleep(0.01) v += 1 f.seek(0) f.write(('%d\n' % v).encode('ASCII')) f.close() lock.close() def many_threads_read_and_write(): r""" >>> with open('f', 'w+b') as file: ... _ = file.write(b'0\n') >>> with open('f.lock', 'w+b') as file: ... _ = file.write(b'0\n') >>> n = 50 >>> threads = [threading.Thread(target=inc) for i in range(n)] >>> _ = [thread.start() for thread in threads] >>> _ = [thread.join() for thread in threads] >>> with open('f', 'rb') as file: ... saved = int(file.read().strip()) >>> saved == n True >>> os.remove('f') We should only have one pid in the lock file: >>> f = open('f.lock') >>> len(f.read().strip().split()) 1 >>> f.close() >>> os.remove('f.lock') """ def pid_in_lockfile(): r""" >>> import os, zc.lockfile >>> pid = os.getpid() >>> lock = zc.lockfile.LockFile("f.lock") >>> f = open("f.lock") >>> _ = f.seek(1) >>> f.read().strip() == str(pid) True >>> f.close() Make sure that locking twice does not overwrite the old pid: >>> lock = zc.lockfile.LockFile("f.lock") Traceback (most recent call last): ... zc.lockfile.LockError: Couldn't lock 'f.lock' >>> f = open("f.lock") >>> _ = f.seek(1) >>> f.read().strip() == str(pid) True >>> f.close() >>> lock.close() """ def hostname_in_lockfile(): r""" hostname is correctly written into the lock file when it's included in the lock file content template >>> import zc.lockfile >>> with patch('socket.gethostname', Mock(return_value='myhostname')): ... lock = zc.lockfile.LockFile( ... "f.lock", content_template='{hostname}') >>> f = open("f.lock") >>> _ = f.seek(1) >>> f.read().rstrip() 'myhostname' >>> f.close() Make sure that locking twice does not overwrite the old hostname: >>> lock = zc.lockfile.LockFile("f.lock", content_template='{hostname}') Traceback (most recent call last): ... zc.lockfile.LockError: Couldn't lock 'f.lock' >>> f = open("f.lock") >>> _ = f.seek(1) >>> f.read().rstrip() 'myhostname' >>> f.close() >>> lock.close() """ class TestLogger: def __init__(self): self.log_entries = [] def exception(self, msg, *args): self.log_entries.append((msg,) + args) class LockFileLogEntryTestCase(unittest.TestCase): """Tests for logging in case of lock failure""" def setUp(self): self.here = os.getcwd() self.tmp = tempfile.mkdtemp(prefix='zc.lockfile-test-') os.chdir(self.tmp) def tearDown(self): os.chdir(self.here) setupstack.rmtree(self.tmp) def test_log_formatting(self): # PID and hostname are parsed and logged from lock file on failure with patch('os.getpid', Mock(return_value=123)): with patch('socket.gethostname', Mock(return_value='myhostname')): lock = zc.lockfile.LockFile( 'f.lock', content_template='{pid}/{hostname}') with open('f.lock') as f: self.assertEqual(' 123/myhostname\n', f.read()) lock.close() def test_unlock_and_lock_while_multiprocessing_process_running(self): import multiprocessing lock = zc.lockfile.LockFile('l') q = multiprocessing.Queue() p = multiprocessing.Process(target=q.get) p.daemon = True p.start() # release and re-acquire should work (obviously) lock.close() lock = zc.lockfile.LockFile('l') self.assertTrue(p.is_alive()) q.put(0) 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() suite.addTest(doctest.DocFileSuite( 'README.txt', setUp=setupstack.setUpDirectory, tearDown=setupstack.tearDown)) suite.addTest(doctest.DocTestSuite( setUp=setupstack.setUpDirectory, tearDown=setupstack.tearDown)) # Add unittest test cases from this module suite.addTest(unittest.defaultTestLoader.loadTestsFromName(__name__)) return suite ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1677569407.1840377 zc.lockfile-3.0.post1/src/zc.lockfile.egg-info/0000755000100100000240000000000014377326577020000 5ustar00macstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569407.0 zc.lockfile-3.0.post1/src/zc.lockfile.egg-info/PKG-INFO0000644000100100000240000001375314377326577021106 0ustar00macstaffMetadata-Version: 2.1 Name: zc.lockfile Version: 3.0.post1 Summary: Basic inter-process locks Home-page: https://github.com/zopefoundation/zc.lockfile Author: Zope Foundation Author-email: zope-dev@zope.dev License: ZPL 2.1 Keywords: lock Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Natural Language :: English Classifier: Operating System :: POSIX Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development Requires-Python: >=3.7 Provides-Extra: test License-File: LICENSE.txt ************************* Basic inter-process locks ************************* The zc.lockfile package provides a basic portable implementation of interprocess locks using lock files. The purpose if not specifically to lock files, but to simply provide locks with an implementation based on file-locking primitives. Of course, these locks could be used to mediate access to *other* files. For example, the ZODB file storage implementation uses file locks to mediate access to file-storage database files. The database files and lock file files are separate files. .. contents:: Detailed Documentation ********************** Lock file support ================= The ZODB lock_file module provides support for creating file system locks. These are locks that are implemented with lock files and OS-provided locking facilities. To create a lock, instantiate a LockFile object with a file name: >>> import zc.lockfile >>> lock = zc.lockfile.LockFile('lock') If we try to lock the same name, we'll get a lock error: >>> import zope.testing.loggingsupport >>> handler = zope.testing.loggingsupport.InstalledHandler('zc.lockfile') >>> try: ... zc.lockfile.LockFile('lock') ... except zc.lockfile.LockError: ... print("Can't lock file") Can't lock file .. We don't log failure to acquire. >>> for record in handler.records: # doctest: +ELLIPSIS ... print(record.levelname+' '+record.getMessage()) To release the lock, use it's close method: >>> lock.close() The lock file is not removed. It is left behind: >>> import os >>> os.path.exists('lock') True Of course, now that we've released the lock, we can create it again: >>> lock = zc.lockfile.LockFile('lock') >>> lock.close() .. Cleanup >>> import os >>> os.remove('lock') Hostname in lock file ===================== In a container environment (e.g. Docker), the PID is typically always identical even if multiple containers are running under the same operating system instance. Clearly, inspecting lock files doesn't then help much in debugging. To identify the container which created the lock file, we need information about the container in the lock file. Since Docker uses the container identifier or name as the hostname, this information can be stored in the lock file in addition to or instead of the PID. Use the ``content_template`` keyword argument to ``LockFile`` to specify a custom lock file content format: >>> lock = zc.lockfile.LockFile('lock', content_template='{pid};{hostname}') >>> lock.close() If you now inspected the lock file, you would see e.g.: $ cat lock 123;myhostname Change History *************** 3.0.post1 (2023-02-28) ====================== - Add ``python_requires`` to ``setup.py`` to prevent installing on not supported old Python versions. 3.0 (2023-02-23) ================ - Add support for Python 3.9, 3.10, 3.11. - Drop support for Python 2.7, 3.5, 3.6. - Drop support for deprecated ``python setup.py test``. 2.0 (2019-08-08) ================ - Extracted new ``SimpleLockFile`` that removes implicit behavior writing to the lock file, and instead allows a subclass to define that behavior. (`#15 `_) - ``SimpleLockFile`` and thus ``LockFile`` are now new-style classes. Any clients relying on ``LockFile`` being an old-style class will need to be adapted. - Drop support for Python 3.4. - Add support for Python 3.8b3. 1.4 (2018-11-12) ================ - Claim support for Python 3.6 and 3.7. - Drop Python 2.6 and 3.3. 1.3.0 (2018-04-23) ================== - Stop logging failure to acquire locks. Clients can do that if they wish. - Claim support for Python 3.4 and 3.5. - Drop Python 3.2 support because pip no longer supports it. 1.2.1 (2016-06-19) ================== - Fixed: unlocking and locking didn't work when a multiprocessing process was running (and presumably other conditions). 1.2.0 (2016-06-09) ================== - Added the ability to include the hostname in the lock file content. - Code and ReST markup cosmetics. [alecghica] 1.1.0 (2013-02-12) ================== - Added Trove classifiers and made setup.py zest.releaser friendly. - Added Python 3.2, 3.3 and PyPy 1.9 support. - Removed Python 2.4 and Python 2.5 support. 1.0.2 (2012-12-02) ================== - Fixed: the fix included in 1.0.1 caused multiple pids to be written to the lock file 1.0.1 (2012-11-30) ================== - Fixed: when there was lock contention, the pid in the lock file was lost. Thanks to Daniel Moisset reporting the problem and providing a fix with tests. - Added test extra to declare test dependency on ``zope.testing``. - Using Python's ``doctest`` module instead of depreacted ``zope.testing.doctest``. 1.0.0 (2008-10-18) ================== - Fixed a small bug in error logging. 1.0.0b1 (2007-07-18) ==================== - Initial release ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569407.0 zc.lockfile-3.0.post1/src/zc.lockfile.egg-info/SOURCES.txt0000644000100100000240000000074214377326577021667 0ustar00macstaffCHANGES.rst CONTRIBUTING.md COPYRIGHT.txt LICENSE.txt MANIFEST.in README.rst setup.cfg setup.py tox.ini src/zc/__init__.py src/zc.lockfile.egg-info/PKG-INFO src/zc.lockfile.egg-info/SOURCES.txt src/zc.lockfile.egg-info/dependency_links.txt src/zc.lockfile.egg-info/namespace_packages.txt src/zc.lockfile.egg-info/not-zip-safe src/zc.lockfile.egg-info/requires.txt src/zc.lockfile.egg-info/top_level.txt src/zc/lockfile/README.txt src/zc/lockfile/__init__.py src/zc/lockfile/tests.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569407.0 zc.lockfile-3.0.post1/src/zc.lockfile.egg-info/dependency_links.txt0000644000100100000240000000000114377326577024046 0ustar00macstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569407.0 zc.lockfile-3.0.post1/src/zc.lockfile.egg-info/namespace_packages.txt0000644000100100000240000000000314377326577024324 0ustar00macstaffzc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569407.0 zc.lockfile-3.0.post1/src/zc.lockfile.egg-info/not-zip-safe0000644000100100000240000000000114377326577022226 0ustar00macstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569407.0 zc.lockfile-3.0.post1/src/zc.lockfile.egg-info/requires.txt0000644000100100000240000000004014377326577022372 0ustar00macstaffsetuptools [test] zope.testing ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569407.0 zc.lockfile-3.0.post1/src/zc.lockfile.egg-info/top_level.txt0000644000100100000240000000000314377326577022523 0ustar00macstaffzc ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1677569406.0 zc.lockfile-3.0.post1/tox.ini0000644000100100000240000000262614377326576014634 0ustar00macstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/pure-python [tox] minversion = 3.18 envlist = lint py37 py38 py39 py310 py311 pypy3 coverage [testenv] usedevelop = true deps = zope.testrunner commands = zope-testrunner --test-path=src {posargs:-vc} extras = test [testenv:lint] basepython = python3 skip_install = true commands = isort --check-only --diff {toxinidir}/src {toxinidir}/setup.py flake8 src setup.py check-manifest check-python-versions deps = check-manifest check-python-versions >= 0.19.1 wheel flake8 isort [testenv:isort-apply] basepython = python3 skip_install = true commands_pre = deps = isort commands = isort {toxinidir}/src {toxinidir}/setup.py [] [testenv:coverage] basepython = python3 allowlist_externals = mkdir deps = coverage zope.testrunner commands = mkdir -p {toxinidir}/parts/htmlcov coverage run -m zope.testrunner --test-path=src {posargs:-vc} coverage html --ignore-errors coverage report --ignore-errors --show-missing --fail-under=96 [coverage:run] branch = True source = zc.lockfile [coverage:report] precision = 2 exclude_lines = pragma: no cover pragma: nocover except ImportError: raise NotImplementedError if __name__ == '__main__': self.fail raise AssertionError [coverage:html] directory = parts/htmlcov