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 <[email protected]>
+
+- 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 <[email protected]>
+
+- 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
<[email protected]>
-
- 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 <[email protected]>
+
+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
<[email protected]>
-
- 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 <[email protected]>
+
+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()
+
+
[email protected]('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]