Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-flufl.lock for openSUSE:Factory checked in at 2021-11-20 02:39:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-flufl.lock (Old) and /work/SRC/openSUSE:Factory/.python-flufl.lock.new.1895 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-flufl.lock" Sat Nov 20 02:39:25 2021 rev:3 rq:932548 version:6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-flufl.lock/python-flufl.lock.changes 2021-02-15 23:17:41.703520797 +0100 +++ /work/SRC/openSUSE:Factory/.python-flufl.lock.new.1895/python-flufl.lock.changes 2021-11-20 02:40:38.348483383 +0100 @@ -1,0 +2,19 @@ +Wed Nov 17 09:13:16 UTC 2021 - Andreas Schneider <a...@cryptomilk.org> + +- Update to version 6.0 + * Added a default_timeout argument to the Lock constructor, + which can be used in the context manager syntax as well. + * When a Lock uses a lock file that already exists and does + not appear to be a lock file (i.e. because its contents are + ill-formatted), do a better job of not clobbering that file. + * Improve some QA by re-adding diff-cover, Gitlab SAST during + CI, and testing on Python 3.10 beta (except for Windows) + * Added a py.typed file to satisfy type checkers. + +------------------------------------------------------------------- +Tue May 25 16:11:12 UTC 2021 - Andreas Schneider <a...@cryptomilk.org> + +- Update to version 5.0.5 + * Blued code. + +------------------------------------------------------------------- Old: ---- flufl.lock-5.0.4.tar.gz New: ---- flufl.lock-6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-flufl.lock.spec ++++++ --- /var/tmp/diff_new_pack.yquFIw/_old 2021-11-20 02:40:38.876481641 +0100 +++ /var/tmp/diff_new_pack.yquFIw/_new 2021-11-20 02:40:38.880481628 +0100 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-flufl.lock -Version: 5.0.4 +Version: 6.0 Release: 0 Summary: NFS-safe file locking with timeouts for POSIX and Windows License: Apache-2.0 @@ -35,8 +35,10 @@ Requires: python-typing_extensions # SECTION test requirements BuildRequires: %{python_module atpublic} +BuildRequires: %{python_module importlib-metadata} BuildRequires: %{python_module psutil} BuildRequires: %{python_module pytest} +BuildRequires: %{python_module six} BuildRequires: %{python_module sybil} # /SECTION BuildArch: noarch ++++++ flufl.lock-5.0.4.tar.gz -> flufl.lock-6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/.readthedocs-req.txt new/flufl.lock-6.0/.readthedocs-req.txt --- old/flufl.lock-5.0.4/.readthedocs-req.txt 2020-08-19 21:54:46.000000000 +0200 +++ new/flufl.lock-6.0/.readthedocs-req.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -sphinx_autodoc_typehints diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/MANIFEST.in new/flufl.lock-6.0/MANIFEST.in --- old/flufl.lock-5.0.4/MANIFEST.in 2021-01-02 03:42:12.000000000 +0100 +++ new/flufl.lock-6.0/MANIFEST.in 2021-08-18 18:55:02.000000000 +0200 @@ -1,8 +1,9 @@ include *.py MANIFEST.in LICENSE README.rst recursive-include test *.py recursive-include docs *.py -global-include *.txt *.rst *.po *.mo *.ini *.cfg -exclude .gitignore +global-include *.txt *.rst *.po *.mo *.ini *.cfg py.typed +exclude .gitignore .readthedocs-req.txt prune build prune .tox prune dist +prune .git diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/PKG-INFO new/flufl.lock-6.0/PKG-INFO --- old/flufl.lock-5.0.4/PKG-INFO 2021-01-02 03:42:37.030737200 +0100 +++ new/flufl.lock-6.0/PKG-INFO 2021-08-18 19:28:45.803283200 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: flufl.lock -Version: 5.0.4 +Version: 6.0 Summary: NFS-safe file locking with timeouts for POSIX and Windows. Home-page: https://flufllock.readthedocs.io Author: Barry Warsaw @@ -10,59 +10,6 @@ Project-URL: Documentation, https://flufllock.readthedocs.io Project-URL: Source, https://gitlab.com/warsaw/flufl.lock.git Project-URL: Tracker, https://gitlab.com/warsaw/flufl.lock/issues -Description: ========== - flufl.lock - ========== - - NFS-safe file locking with timeouts for POSIX and Windows. - - The ``flufl.lock`` library provides an NFS-safe file-based locking algorithm - influenced by the GNU/Linux ``open(2)`` manpage, under the description of the - ``O_EXCL`` option. - - [...] O_EXCL is broken on NFS file systems, programs which rely on it - for performing locking tasks will contain a race condition. The - solution for performing atomic file locking using a lockfile is to - create a unique file on the same fs (e.g., incorporating hostname and - pid), use link(2) to make a link to the lockfile. If link() returns - 0, the lock is successful. Otherwise, use stat(2) on the unique file - to check if its link count has increased to 2, in which case the lock - is also successful. - - The assumption made here is that there will be no *outside interference*, - e.g. no agent external to this code will ever ``link()`` to the specific lock - files used. - - Lock objects support lock-breaking so that you can't wedge a process forever. - This is especially helpful in a web environment, but may not be appropriate - for all applications. - - Locks have a *lifetime*, which is the maximum length of time the process - expects to retain the lock. It is important to pick a good number here - because other processes will not break an existing lock until the expected - lifetime has expired. Too long and other processes will hang; too short and - you'll end up trampling on existing process locks -- and possibly corrupting - data. In a distributed (NFS) environment, you also need to make sure that - your clocks are properly synchronized. - - - Author - ====== - - ``flufl.lock`` is Copyright (C) 2007-2021 Barry Warsaw <ba...@python.org> - - Licensed under the terms of the Apache License Version 2.0. See the LICENSE - file for details. - - - Project details - =============== - - * Project home: https://gitlab.com/warsaw/flufl.lock - * Report bugs at: https://gitlab.com/warsaw/flufl.lock/issues - * Code hosting: https://gitlab.com/warsaw/flufl.lock.git - * Documentation: https://flufllock.readthedocs.io/ - Keywords: locking locks lock Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable @@ -78,3 +25,59 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.6 Description-Content-Type: text/x-rst +License-File: LICENSE + +========== +flufl.lock +========== + +NFS-safe file locking with timeouts for POSIX and Windows. + +The ``flufl.lock`` library provides an NFS-safe file-based locking algorithm +influenced by the GNU/Linux ``open(2)`` manpage, under the description of the +``O_EXCL`` option. + + [...] O_EXCL is broken on NFS file systems, programs which rely on it + for performing locking tasks will contain a race condition. The + solution for performing atomic file locking using a lockfile is to + create a unique file on the same fs (e.g., incorporating hostname and + pid), use link(2) to make a link to the lockfile. If link() returns + 0, the lock is successful. Otherwise, use stat(2) on the unique file + to check if its link count has increased to 2, in which case the lock + is also successful. + +The assumption made here is that there will be no *outside interference*, +e.g. no agent external to this code will ever ``link()`` to the specific lock +files used. + +Lock objects support lock-breaking so that you can't wedge a process forever. +This is especially helpful in a web environment, but may not be appropriate +for all applications. + +Locks have a *lifetime*, which is the maximum length of time the process +expects to retain the lock. It is important to pick a good number here +because other processes will not break an existing lock until the expected +lifetime has expired. Too long and other processes will hang; too short and +you'll end up trampling on existing process locks -- and possibly corrupting +data. In a distributed (NFS) environment, you also need to make sure that +your clocks are properly synchronized. + + +Author +====== + +``flufl.lock`` is Copyright (C) 2007-2021 Barry Warsaw <ba...@python.org> + +Licensed under the terms of the Apache License Version 2.0. See the LICENSE +file for details. + + +Project details +=============== + + * Project home: https://gitlab.com/warsaw/flufl.lock + * Report bugs at: https://gitlab.com/warsaw/flufl.lock/issues + * Code hosting: https://gitlab.com/warsaw/flufl.lock.git + * Documentation: https://flufllock.readthedocs.io/ + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/docs/NEWS.rst new/flufl.lock-6.0/docs/NEWS.rst --- old/flufl.lock-5.0.4/docs/NEWS.rst 2021-01-02 03:42:12.000000000 +0100 +++ new/flufl.lock-6.0/docs/NEWS.rst 2021-08-18 18:52:53.000000000 +0200 @@ -2,6 +2,25 @@ NEWS for flufl.lock =================== +6.0 (2021-08-18) +================ +* Added a ``default_timeout`` argument to the ``Lock`` constructor, which can + be used in the context manager syntax as well. (GL#24) +* When a ``Lock`` uses a lock file that already exists and does not appear to + be a lock file (i.e. because its contents are ill-formatted), do a better + job of not clobbering that file. (GL#25) +* Improve some QA by re-adding diff-cover, Gitlab SAST during CI, and testing + on Python 3.10 beta (except for Windows) +* The ``master`` branch is renamed to ``main``. (GL#28) + +5.1 (2021-05-28) +================ +* Added a ``py.typed`` file to satisfy type checkers. (GL#27) + +5.0.5 (2021-02-12) +================== +* I `blue <https://blue.readthedocs.io/en/latest/>`_ it! + 5.0.4 (2021-01-01) ================== * Update copyright years. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/docs/theory.rst new/flufl.lock-6.0/docs/theory.rst --- old/flufl.lock-5.0.4/docs/theory.rst 2020-08-22 01:47:58.000000000 +0200 +++ new/flufl.lock-6.0/docs/theory.rst 2021-08-18 18:40:35.000000000 +0200 @@ -30,7 +30,8 @@ When a :class:`Lock` object is instantiated, the user names a file system path in the constructor. This is the file that all processes will synchronize -on when attempting to acquire the lock. We call this the *lock file*. +on when attempting to acquire the lock. We call this the *lock file*. This +file should not already exist. Locks have a *lifetime* which is the period of time that the process expects to keep the lock, once it has been acquired. This lifetime is used to diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/docs/using.rst new/flufl.lock-6.0/docs/using.rst --- old/flufl.lock-5.0.4/docs/using.rst 2020-08-22 01:47:58.000000000 +0200 +++ new/flufl.lock-6.0/docs/using.rst 2021-08-18 18:40:35.000000000 +0200 @@ -26,7 +26,7 @@ To create a lock, you must first instantiate a :class:`Lock` object, specifying the path to a file that will be used to synchronize the lock. This -file should not exist. +file should not already exist. :: # This function comes from the test infrastructure. @@ -153,6 +153,38 @@ False +Time outs +========= + +When attempting to acquire a lock, you can specify a timeout interval as +either an integer number of seconds, or as a :class:`datetime.timedelta`. +If the lock is not acquired within this interval, a :class:`TimeOutError` is +raised. + +You can specify a default timeout interval in the :class:`Lock` constructor. + + >>> from flufl.lock import TimeOutError + >>> acquire(filename, lifetime=5) + >>> try: + ... with Lock(filename, default_timeout=1) as my_lock: + ... pass + ... except TimeOutError: + ... print('Timed out, as expected') + Timed out, as expected + +You can also specify a timeout interval in the :func:`Lock.lock` call. This +overrides the constructor argument. + + >>> acquire(filename, lifetime=5) + >>> my_lock = Lock(filename, default_timeout=1) + >>> try: + ... my_lock.lock(timeout=10) + ... my_lock.is_locked + ... finally: + ... my_lock.unlock() + True + + Lock details ============ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/flufl/lock/__init__.py new/flufl.lock-6.0/flufl/lock/__init__.py --- old/flufl.lock-5.0.4/flufl/lock/__init__.py 2021-01-02 03:42:12.000000000 +0100 +++ new/flufl.lock-6.0/flufl/lock/__init__.py 2021-08-18 00:41:50.000000000 +0200 @@ -1,11 +1,17 @@ from public import public as _public from flufl.lock._lockfile import ( - AlreadyLockedError, Lock, LockError, LockState, NotLockedError, SEP, - TimeOutError) + AlreadyLockedError, + Lock, + LockError, + LockState, + NotLockedError, + SEP, + TimeOutError, +) -__version__ = '5.0.4' +__version__ = '6.0' _public( @@ -17,7 +23,7 @@ SEP=SEP, TimeOutError=TimeOutError, __version__=__version__, - ) +) del _public diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/flufl/lock/_lockfile.py new/flufl.lock-6.0/flufl/lock/_lockfile.py --- old/flufl.lock-5.0.4/flufl/lock/_lockfile.py 2020-08-21 02:26:54.000000000 +0200 +++ new/flufl.lock-6.0/flufl/lock/_lockfile.py 2021-08-18 18:40:35.000000000 +0200 @@ -37,7 +37,7 @@ # Details separator; also used in calculating the claim file path. Lock files # should not include this character. We do it like this so flake8 won't # complain about SEP. -SEP = ('^' if sys.platform == 'win32' else '|') +SEP = '^' if sys.platform == 'win32' else '|' public(SEP=SEP) # LP: #977999 - catch both ENOENT and ESTALE. The latter is what an NFS @@ -82,6 +82,7 @@ lock. Note that the policy on what to do with this information is left entirely to the user of the library. """ + #: There is no lock file so the lock is unlocked. unlocked = 1 #: We own the lock and it is fresh. @@ -102,6 +103,16 @@ unknown = 6 +def _interval_to_datetime( + timeout: Optional[Interval] = None, +) -> Optional[datetime]: + if timeout is None: + return None + if isinstance(timeout, int): + timeout = timedelta(seconds=timeout) + return datetime.now() + timeout + + @public class Lock: """Portable, NFS-safe file locking with timeouts for POSIX systems. @@ -160,19 +171,24 @@ :param separator: The separator character to use in the lock file's file name. Defaults to the vertical bar (`|`) on POSIX systems and caret (`^`) on Windows. - """ + :param default_timeout: Default timeout for approximately how long the lock + acquisition attempt should be made. The value given in the `.lock()` + call always overrides this. + """ def __init__( - self, - lockfile: str, - lifetime: Optional[Interval] = None, - separator: str = SEP - ): + self, + lockfile: str, + lifetime: Optional[Interval] = None, + separator: str = SEP, + default_timeout: Optional[Interval] = None, + ): """Create the resource lock using the given file name and lifetime.""" # The hostname has to be defined before we call _set_claimfile(). self._hostname = socket.getfqdn() if lifetime is None: lifetime = DEFAULT_LOCK_LIFETIME + self._default_timeout = default_timeout self._lockfile = lockfile # https://github.com/python/mypy/issues/3004 self.lifetime = lifetime # type: ignore @@ -188,7 +204,10 @@ self.__class__.__name__, self._lockfile, ('locked' if self._is_locked_no_refresh() else 'unlocked'), - self._lifetime, os.getpid(), id(self)) + self._lifetime, + os.getpid(), + id(self), + ) @property def hostname(self) -> str: @@ -216,7 +235,8 @@ # Rearrange for signature. try: lockfile, hostname, pid, random_ignored = filename.split( - self._separator) + self._separator + ) except ValueError as error: raise NotLockedError('Details are unavailable') from error return hostname, int(pid), lockfile @@ -231,7 +251,8 @@ return LockState.unlocked try: lockfile, hostname, pid_str, random_ignored = filename.split( - self._separator) + self._separator + ) pid = int(pid_str) except (ValueError, TypeError): # The contents of the lock file is corrupt, so we can't know @@ -259,11 +280,12 @@ else: self._lifetime = timedelta(seconds=lifetime) - def refresh(self, - lifetime: Optional[Interval] = None, - *, - unconditionally: bool = False - ) -> None: + def refresh( + self, + lifetime: Optional[Interval] = None, + *, + unconditionally: bool = False + ) -> None: """Refreshes the lifetime of a locked file. Use this if you realize that you need to keep a resource locked longer @@ -299,10 +321,9 @@ :raises TimeOutError: if ``timeout`` is not None and the indicated time interval expires without a lock acquisition. """ - if timeout is not None: - if isinstance(timeout, int): - timeout = timedelta(seconds=timeout) - timeout_time = datetime.now() + timeout + timeout_time = _interval_to_datetime( + self._default_timeout if timeout is None else timeout + ) # Make sure the claim file exists, and that its contents are current. self._write() # XXX This next call can fail with an EPERM. I have no idea why, but @@ -348,8 +369,9 @@ elif self._linkcount != 2: # Somebody's messin' with us! Log this, and try again # later. XXX should we raise an exception? - log.error('unexpected linkcount: {0:d}'.format( - self._linkcount)) + log.error( + 'unexpected linkcount: {0:d}'.format(self._linkcount) + ) elif self._read() == self._claimfile: # It was us that already had the link. log.debug('already locked: {}'.format(self._lockfile)) @@ -358,7 +380,7 @@ pass # We did not acquire the lock, because someone else already has # it. Have we timed out in our quest for the lock? - if timeout is not None and timeout_time < datetime.now(): + if timeout_time is not None and timeout_time < datetime.now(): os.unlink(self._claimfile) log.error('timed out') raise TimeOutError('Could not acquire the lock') @@ -439,11 +461,12 @@ self.lock() return self - def __exit__(self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType] - ) -> Literal[False]: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Literal[False]: self.unlock() # Don't suppress any exception that might have occurred. return False @@ -454,12 +477,14 @@ # the lock. We need to watch out for two Lock objects in the same # process pointing to the same lock file. Without this, if you lock # lf1 and do not lock lf2, lf2.locked() will still return True. - self._claimfile = self._separator.join(( - self._lockfile, - self.hostname, - str(os.getpid()), - str(random.randint(0, MAXINT)), - )) + self._claimfile = self._separator.join( + ( + self._lockfile, + self.hostname, + str(os.getpid()), + str(random.randint(0, MAXINT)), + ) + ) def _write(self) -> None: """Write our claim file's name to the claim file.""" @@ -554,7 +579,23 @@ def _break(self) -> None: """Break the lock.""" - # First, touch the lock file. This reduces but does not eliminate the + # Try to read from the lock file. All we care about is that its + # contents have the details expected of any lock file. If not, then + # this probably isn't a lock that needs breaking, it's a Lock with a + # lock file pointing to an existing, unrelated file. Refuse to break + # that lock. All we really need to do is to log and return. If a + # timeout was given, eventually the .lock() call will timeout. + # However if no timeout was given, the .lock() will block forever. + try: + self.details + except NotLockedError: + log.error( + "lockfile exists but isn't safe to break: {}".format( + self._lockfile + ) + ) + return + # Touch the lock file. This reduces but does not eliminate the # chance for a race condition during breaking. Two processes could # both pass the test for lock expiry in lock() before one of them gets # to touch the lock file. This shouldn't be too bad because all diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/flufl.lock.egg-info/PKG-INFO new/flufl.lock-6.0/flufl.lock.egg-info/PKG-INFO --- old/flufl.lock-5.0.4/flufl.lock.egg-info/PKG-INFO 2021-01-02 03:42:36.000000000 +0100 +++ new/flufl.lock-6.0/flufl.lock.egg-info/PKG-INFO 2021-08-18 19:28:45.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: flufl.lock -Version: 5.0.4 +Version: 6.0 Summary: NFS-safe file locking with timeouts for POSIX and Windows. Home-page: https://flufllock.readthedocs.io Author: Barry Warsaw @@ -10,59 +10,6 @@ Project-URL: Documentation, https://flufllock.readthedocs.io Project-URL: Source, https://gitlab.com/warsaw/flufl.lock.git Project-URL: Tracker, https://gitlab.com/warsaw/flufl.lock/issues -Description: ========== - flufl.lock - ========== - - NFS-safe file locking with timeouts for POSIX and Windows. - - The ``flufl.lock`` library provides an NFS-safe file-based locking algorithm - influenced by the GNU/Linux ``open(2)`` manpage, under the description of the - ``O_EXCL`` option. - - [...] O_EXCL is broken on NFS file systems, programs which rely on it - for performing locking tasks will contain a race condition. The - solution for performing atomic file locking using a lockfile is to - create a unique file on the same fs (e.g., incorporating hostname and - pid), use link(2) to make a link to the lockfile. If link() returns - 0, the lock is successful. Otherwise, use stat(2) on the unique file - to check if its link count has increased to 2, in which case the lock - is also successful. - - The assumption made here is that there will be no *outside interference*, - e.g. no agent external to this code will ever ``link()`` to the specific lock - files used. - - Lock objects support lock-breaking so that you can't wedge a process forever. - This is especially helpful in a web environment, but may not be appropriate - for all applications. - - Locks have a *lifetime*, which is the maximum length of time the process - expects to retain the lock. It is important to pick a good number here - because other processes will not break an existing lock until the expected - lifetime has expired. Too long and other processes will hang; too short and - you'll end up trampling on existing process locks -- and possibly corrupting - data. In a distributed (NFS) environment, you also need to make sure that - your clocks are properly synchronized. - - - Author - ====== - - ``flufl.lock`` is Copyright (C) 2007-2021 Barry Warsaw <ba...@python.org> - - Licensed under the terms of the Apache License Version 2.0. See the LICENSE - file for details. - - - Project details - =============== - - * Project home: https://gitlab.com/warsaw/flufl.lock - * Report bugs at: https://gitlab.com/warsaw/flufl.lock/issues - * Code hosting: https://gitlab.com/warsaw/flufl.lock.git - * Documentation: https://flufllock.readthedocs.io/ - Keywords: locking locks lock Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable @@ -78,3 +25,59 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.6 Description-Content-Type: text/x-rst +License-File: LICENSE + +========== +flufl.lock +========== + +NFS-safe file locking with timeouts for POSIX and Windows. + +The ``flufl.lock`` library provides an NFS-safe file-based locking algorithm +influenced by the GNU/Linux ``open(2)`` manpage, under the description of the +``O_EXCL`` option. + + [...] O_EXCL is broken on NFS file systems, programs which rely on it + for performing locking tasks will contain a race condition. The + solution for performing atomic file locking using a lockfile is to + create a unique file on the same fs (e.g., incorporating hostname and + pid), use link(2) to make a link to the lockfile. If link() returns + 0, the lock is successful. Otherwise, use stat(2) on the unique file + to check if its link count has increased to 2, in which case the lock + is also successful. + +The assumption made here is that there will be no *outside interference*, +e.g. no agent external to this code will ever ``link()`` to the specific lock +files used. + +Lock objects support lock-breaking so that you can't wedge a process forever. +This is especially helpful in a web environment, but may not be appropriate +for all applications. + +Locks have a *lifetime*, which is the maximum length of time the process +expects to retain the lock. It is important to pick a good number here +because other processes will not break an existing lock until the expected +lifetime has expired. Too long and other processes will hang; too short and +you'll end up trampling on existing process locks -- and possibly corrupting +data. In a distributed (NFS) environment, you also need to make sure that +your clocks are properly synchronized. + + +Author +====== + +``flufl.lock`` is Copyright (C) 2007-2021 Barry Warsaw <ba...@python.org> + +Licensed under the terms of the Apache License Version 2.0. See the LICENSE +file for details. + + +Project details +=============== + + * Project home: https://gitlab.com/warsaw/flufl.lock + * Report bugs at: https://gitlab.com/warsaw/flufl.lock/issues + * Code hosting: https://gitlab.com/warsaw/flufl.lock.git + * Documentation: https://flufllock.readthedocs.io/ + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/flufl.lock.egg-info/SOURCES.txt new/flufl.lock-6.0/flufl.lock.egg-info/SOURCES.txt --- old/flufl.lock-5.0.4/flufl.lock.egg-info/SOURCES.txt 2021-01-02 03:42:36.000000000 +0100 +++ new/flufl.lock-6.0/flufl.lock.egg-info/SOURCES.txt 2021-08-18 19:28:45.000000000 +0200 @@ -1,4 +1,3 @@ -.readthedocs-req.txt LICENSE MANIFEST.in README.rst @@ -23,6 +22,7 @@ flufl.lock.egg-info/top_level.txt flufl/lock/__init__.py flufl/lock/_lockfile.py +flufl/lock/py.typed test/__init__.py test/test_api.py test/test_lock.py \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/setup.cfg new/flufl.lock-6.0/setup.cfg --- old/flufl.lock-5.0.4/setup.cfg 2021-01-02 03:42:37.032559900 +0100 +++ new/flufl.lock-6.0/setup.cfg 2021-08-18 19:28:45.804479100 +0200 @@ -1,10 +1,9 @@ [tool:pytest] -addopts = --cov=flufl +addopts = --cov=flufl --cov-report=term --cov-report=xml testpaths = test docs [flake8] exclude = conf.py -hang-closing = true jobs = 1 max-line-length = 79 @@ -22,13 +21,14 @@ flufl/lock [tool:isort] +include_trailing_comma = true +known_first_party = flufl length_sort_straight = true lines_after_imports = 2 lines_between_types = 1 -multi_line_output = 4 +multi_line_output = 3 order_by_type = false skip = conf.py -known_first_party = flufl [mypy] namespace_packages = true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/setup.py new/flufl.lock-6.0/setup.py --- old/flufl.lock-5.0.4/setup.py 2020-11-19 18:44:08.000000000 +0100 +++ new/flufl.lock-6.0/setup.py 2021-05-29 18:31:03.000000000 +0200 @@ -27,6 +27,9 @@ packages=find_namespace_packages(where='.', exclude=['test*', 'docs']), namespace_packages=['flufl'], include_package_data=True, + package_data={ + 'flufl.lock': ['flufl/lock/py.typed'], + }, # readthedocs builds fail unless zip_safe is False. zip_safe=False, python_requires='>=3.6', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/test/test_lock.py new/flufl.lock-6.0/test/test_lock.py --- old/flufl.lock-5.0.4/test/test_lock.py 2020-11-19 18:44:08.000000000 +0100 +++ new/flufl.lock-6.0/test/test_lock.py 2021-08-18 18:40:35.000000000 +0200 @@ -19,6 +19,7 @@ import pytest from flufl.lock import Lock, LockState, NotLockedError, SEP, TimeOutError +from flufl.lock._lockfile import CLOCK_SLOP EMOCKEDFAILURE = 99 @@ -188,9 +189,9 @@ lock.refresh(unconditionally=True) -def child_locker(filename, queue, sleep=3): +def child_locker(filename, queue, sleep=3, lifetime=15): with suppress(NotLockedError): - with Lock(filename, lifetime=15): + with Lock(filename, lifetime=lifetime): queue.put(True) time.sleep(sleep) @@ -435,3 +436,59 @@ with pytest.raises(OSError) as excinfo: lock.is_locked assert excinfo.value.errno == 999 + + +def test_lock_constructor_with_timeout(lock): + # Pass an optional timeout value to the constructor. + queue = Queue() + Process(target=child_locker, args=(lock.lockfile, queue)).start() + # Wait for the child process to acquire the lock. + queue.get() + with pytest.raises(TimeOutError): + with Lock(lock.lockfile, default_timeout=1): + pass + + +def test_lock_constructor_with_timeout_override(lock): + # Explicit timeout in the lock() call overrides constructor timeout. + queue = Queue() + Process(target=child_locker, + # Give the child lock a lifetime of 5 seconds. We'll provide a + # shorter timeout in the constructor, which should time out, but a + # longer time in the lock() call which will result in acquiring + # the lock when the lifetime of the child expires. + args=(lock.lockfile, queue, 3, 5) + ).start() + # Wait for the child process to acquire the lock. + queue.get() + my_lock = Lock(lock.lockfile, default_timeout=1) + try: + my_lock.lock(timeout=10) + assert my_lock.is_locked + finally: + my_lock.unlock() + + +@pytest.mark.parametrize('lifetime', [1, 5]) +def test_use_unrelated_existing_lockfile(lock, lifetime): + # If someone gives a lock file that already exists, and that isn't a + # related lock file, then trying to lock it shouldn't destroy the existing + # file. + # + # https://gitlab.com/warsaw/flufl.lock/-/issues/25 + # + # There are two cases, one where the lock's lifetime is less than the + # timeout value and one where the lifetime is greater than the timeout + # value. In both cases, the expiration time should be in the past and both + # should preserve the original (non-)lockfile. + lock.lifetime = lifetime + with open(lock.lockfile, 'w') as fp: + fp.write('save me') + # Put the lock file's release time in the past. This has to include the + # clock slop factor. + past = time.time() - lifetime - CLOCK_SLOP.seconds + os.utime(lock.lockfile, (past, past)) + with pytest.raises(TimeOutError): + lock.lock(timeout=3) + with open(lock.lockfile) as fp: + assert fp.read() == 'save me' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/flufl.lock-5.0.4/tox.ini new/flufl.lock-6.0/tox.ini --- old/flufl.lock-5.0.4/tox.ini 2020-08-22 01:47:58.000000000 +0200 +++ new/flufl.lock-6.0/tox.ini 2021-08-09 19:03:05.000000000 +0200 @@ -1,13 +1,15 @@ [tox] -envlist = {py36,py37,py38,py39},qa,docs +envlist = {py36,py37,py38,py39,py310},qa,docs skip_missing_interpreters = True [testenv] commands = python -m pytest {posargs} + diff-cover coverage.xml usedevelop = True setenv = PYTHONPATH = '' deps = + diff-cover pytest pytest-cov sybil @@ -17,11 +19,13 @@ commands = python -m flake8 flufl/lock isort flufl/lock + blue --check flufl/lock mypy -p flufl.lock deps = flake8 isort>=5.4.1 mypy + blue>=0.6.0 [testenv:docs] basepython = python3 ++++++ python-flufl.lock-fix-setup.patch ++++++ --- /var/tmp/diff_new_pack.yquFIw/_old 2021-11-20 02:40:39.012481192 +0100 +++ /var/tmp/diff_new_pack.yquFIw/_new 2021-11-20 02:40:39.016481179 +0100 @@ -1,10 +1,10 @@ -Index: flufl.lock-5.0.4/setup.cfg +Index: flufl.lock-6.0/setup.cfg =================================================================== ---- flufl.lock-5.0.4.orig/setup.cfg 2021-01-02 03:42:37.032559900 +0100 -+++ flufl.lock-5.0.4/setup.cfg 2021-02-11 09:11:24.784359755 +0100 +--- flufl.lock-6.0.orig/setup.cfg 2021-11-17 10:15:03.278762919 +0100 ++++ flufl.lock-6.0/setup.cfg 2021-11-17 10:15:21.578893997 +0100 @@ -1,5 +1,4 @@ [tool:pytest] --addopts = --cov=flufl +-addopts = --cov=flufl --cov-report=term --cov-report=xml testpaths = test docs [flake8]