Hello community,
here is the log from the commit of package python-fasteners for
openSUSE:Factory checked in at 2019-06-13 22:31:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-fasteners (Old)
and /work/SRC/openSUSE:Factory/.python-fasteners.new.4811 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-fasteners"
Thu Jun 13 22:31:51 2019 rev:6 rq:708374 version:0.15
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-fasteners/python-fasteners.changes
2018-12-13 19:44:19.581021845 +0100
+++
/work/SRC/openSUSE:Factory/.python-fasteners.new.4811/python-fasteners.changes
2019-06-13 22:31:55.268409065 +0200
@@ -1,0 +2,9 @@
+Fri Jun 7 12:56:34 UTC 2019 - Marketa Calabkova <[email protected]>
+
+- update to version 0.15
+ * Welcome back alive!
+ * Drop Python 2.6
+ * TST: check exitcodes of child processes
+ * TST: fix multiplatform support for process lock tests
+
+-------------------------------------------------------------------
Old:
----
fasteners-0.14.1.tar.gz
New:
----
fasteners-0.15.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-fasteners.spec ++++++
--- /var/tmp/diff_new_pack.AQmLK0/_old 2019-06-13 22:31:55.796408893 +0200
+++ /var/tmp/diff_new_pack.AQmLK0/_new 2019-06-13 22:31:55.796408893 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-fasteners
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%bcond_without tests
Name: python-fasteners
-Version: 0.14.1
+Version: 0.15
Release: 0
Summary: A python package that provides useful locks
License: Apache-2.0
++++++ fasteners-0.14.1.tar.gz -> fasteners-0.15.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/fasteners-0.14.1/PKG-INFO new/fasteners-0.15/PKG-INFO
--- old/fasteners-0.14.1/PKG-INFO 2015-11-13 07:47:25.000000000 +0100
+++ new/fasteners-0.15/PKG-INFO 2019-05-20 19:36:52.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: fasteners
-Version: 0.14.1
+Version: 0.15
Summary: A python package that provides useful locks.
Home-page: https://github.com/harlowja/fasteners
Author: Joshua Harlow
@@ -12,6 +12,9 @@
.. image:: https://travis-ci.org/harlowja/fasteners.png?branch=master
:target: https://travis-ci.org/harlowja/fasteners
+ .. image:: https://ci.appveyor.com/api/projects/status/7d7aku32pimpadiv
+ :target: https://ci.appveyor.com/project/JoshuaHarlow/fasteners
+
.. image::
https://readthedocs.org/projects/fasteners/badge/?version=latest
:target: https://readthedocs.org/projects/fasteners/?badge=latest
:alt: Documentation Status
@@ -71,7 +74,7 @@
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/fasteners-0.14.1/README.rst
new/fasteners-0.15/README.rst
--- old/fasteners-0.14.1/README.rst 2015-08-08 10:15:25.000000000 +0200
+++ new/fasteners-0.15/README.rst 2019-04-26 21:18:09.000000000 +0200
@@ -4,6 +4,9 @@
.. image:: https://travis-ci.org/harlowja/fasteners.png?branch=master
:target: https://travis-ci.org/harlowja/fasteners
+.. image:: https://ci.appveyor.com/api/projects/status/7d7aku32pimpadiv
+ :target: https://ci.appveyor.com/project/JoshuaHarlow/fasteners
+
.. image:: https://readthedocs.org/projects/fasteners/badge/?version=latest
:target: https://readthedocs.org/projects/fasteners/?badge=latest
:alt: Documentation Status
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/fasteners-0.14.1/fasteners/_utils.py
new/fasteners-0.15/fasteners/_utils.py
--- old/fasteners-0.14.1/fasteners/_utils.py 2015-11-13 07:39:16.000000000
+0100
+++ new/fasteners-0.15/fasteners/_utils.py 2019-04-26 21:18:09.000000000
+0200
@@ -17,8 +17,25 @@
# under the License.
import logging
+import sys
import time
+try:
+ from os import fsencode as _fsencode
+except (ImportError, AttributeError):
+ def _fsencode(path):
+ # Replicate similar logic to what py3.2+ fsencode does.
+ # See: https://bugs.python.org/issue8514
+ encoding = sys.getfilesystemencoding()
+ if encoding == 'mbcs':
+ errors = 'strict'
+ else:
+ errors = 'surrogateescape'
+ return path.encode(encoding, errors)
+
+
+import six
+
from monotonic import monotonic as now # noqa
# log level for low-level debugging
@@ -27,6 +44,19 @@
LOG = logging.getLogger(__name__)
+def canonicalize_path(path):
+ """Canonicalizes a potential path.
+
+ Returns a binary string encoded into filesystem encoding.
+ """
+ if isinstance(path, six.binary_type):
+ return path
+ if isinstance(path, six.text_type):
+ return _fsencode(path)
+ else:
+ return canonicalize_path(str(path))
+
+
def pick_first_not_none(*values):
"""Returns first of values that is *not* None (or None if all are/were)."""
for val in values:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/fasteners-0.14.1/fasteners/lock.py
new/fasteners-0.15/fasteners/lock.py
--- old/fasteners-0.14.1/fasteners/lock.py 2015-11-13 07:39:16.000000000
+0100
+++ new/fasteners-0.15/fasteners/lock.py 2019-04-26 21:18:09.000000000
+0200
@@ -119,19 +119,6 @@
#: Reader owner type/string constant.
READER = 'r'
- @staticmethod
- def _fetch_current_thread_functor():
- # Until https://github.com/eventlet/eventlet/issues/172 is resolved
- # or addressed we have to use complicated workaround to get a object
- # that will not be recycled; the usage of threading.current_thread()
- # doesn't appear to currently be monkey patched and therefore isn't
- # reliable to use (and breaks badly when used as all threads share
- # the same current_thread() object)...
- if eventlet is not None and eventlet_patcher is not None:
- if eventlet_patcher.is_monkey_patched('thread'):
- return eventlet.getcurrent
- return threading.current_thread
-
def __init__(self,
condition_cls=threading.Condition,
current_thread_functor=None):
@@ -139,9 +126,7 @@
self._pending_writers = collections.deque()
self._readers = {}
self._cond = condition_cls()
- if current_thread_functor is None:
- current_thread_functor = self._fetch_current_thread_functor()
- self._current_thread = current_thread_functor
+ self._current_thread = threading.current_thread
@property
def has_pending_writers(self):
@@ -222,6 +207,8 @@
Will wait until no active readers. Blocks readers after acquiring.
+ Guaranteed for locks to be processed in fair order (FIFO).
+
Raises a ``RuntimeError`` if an active reader attempts to acquire
a lock.
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/fasteners-0.14.1/fasteners/process_lock.py
new/fasteners-0.15/fasteners/process_lock.py
--- old/fasteners-0.14.1/fasteners/process_lock.py 2015-11-13
07:39:16.000000000 +0100
+++ new/fasteners-0.15/fasteners/process_lock.py 2019-04-26
21:18:09.000000000 +0200
@@ -66,6 +66,11 @@
safe to close the file descriptor while another thread holds the
lock. Just opening and closing the lock file can break synchronization,
so lock files must be accessed only using this abstraction.
+
+ .. warning::
+
+ It is quite useful to read before using (to understand
+ the risks involved): http://0pointer.de/blog/projects/locking.html
"""
MAX_DELAY = 0.1
@@ -84,7 +89,7 @@
def __init__(self, path, sleep_func=time.sleep, logger=None):
self.lockfile = None
- self.path = path
+ self.path = _utils.canonicalize_path(path)
self.acquired = False
self.sleep_func = sleep_func
self.logger = _utils.pick_first_not_none(logger, LOG)
@@ -171,7 +176,12 @@
self.lockfile = None
def __enter__(self):
- self.acquire()
+ gotten = self.acquire()
+ if not gotten:
+ # This shouldn't happen, but just incase...
+ raise threading.ThreadError("Unable to acquire a file lock"
+ " on `%s` (when used as a"
+ " context manager)" % self.path)
return self
def release(self):
@@ -204,30 +214,44 @@
return os.path.exists(self.path)
def trylock(self):
- raise NotImplementedError()
+ self._trylock(self.lockfile)
def unlock(self):
+ self._unlock(self.lockfile)
+
+ @staticmethod
+ def _trylock():
+ raise NotImplementedError()
+
+ @staticmethod
+ def _unlock():
raise NotImplementedError()
class _WindowsLock(_InterProcessLock):
"""Interprocess lock implementation that works on windows systems."""
- def trylock(self):
- msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_NBLCK, 1)
-
- def unlock(self):
- msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
+ @staticmethod
+ def _trylock(lockfile):
+ fileno = lockfile.fileno()
+ msvcrt.locking(fileno, msvcrt.LK_NBLCK, 1)
+
+ @staticmethod
+ def _unlock(lockfile):
+ fileno = lockfile.fileno()
+ msvcrt.locking(fileno, msvcrt.LK_UNLCK, 1)
class _FcntlLock(_InterProcessLock):
"""Interprocess lock implementation that works on posix systems."""
- def trylock(self):
- fcntl.lockf(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
-
- def unlock(self):
- fcntl.lockf(self.lockfile, fcntl.LOCK_UN)
+ @staticmethod
+ def _trylock(lockfile):
+ fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+ @staticmethod
+ def _unlock(lockfile):
+ fcntl.lockf(lockfile, fcntl.LOCK_UN)
if os.name == 'nt':
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/fasteners-0.14.1/fasteners/tests/test_process_lock.py
new/fasteners-0.15/fasteners/tests/test_process_lock.py
--- old/fasteners-0.14.1/fasteners/tests/test_process_lock.py 2015-08-08
10:15:25.000000000 +0200
+++ new/fasteners-0.15/fasteners/tests/test_process_lock.py 2019-04-26
21:18:09.000000000 +0200
@@ -15,12 +15,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+import contextlib
import errno
-import fcntl
import multiprocessing
import os
import shutil
-import signal
+import sys
import tempfile
import threading
import time
@@ -28,6 +28,8 @@
from fasteners import process_lock as pl
from fasteners import test
+WIN32 = os.name == 'nt'
+
class BrokenLock(pl.InterProcessLock):
def __init__(self, name, errno_code):
@@ -43,6 +45,87 @@
raise err
[email protected]
+def scoped_child_processes(children, timeout=0.1, exitcode=0):
+ for child in children:
+ child.daemon = True
+ child.start()
+ yield
+ start = time.time()
+ timed_out = 0
+
+ for child in children:
+ child.join(max(timeout - (time.time() - start), 0))
+ if child.is_alive():
+ timed_out += 1
+ child.terminate()
+
+ if timed_out:
+ msg = "{} child processes killed due to timeout\n".format(timed_out)
+ sys.stderr.write(msg)
+
+ if exitcode is not None:
+ for child in children:
+ c_code = child.exitcode
+ msg = "Child exitcode {} != {}"
+ assert c_code == exitcode, msg.format(c_code, exitcode)
+
+
+def try_lock(lock_file):
+ try:
+ my_lock = pl.InterProcessLock(lock_file)
+ my_lock.lockfile = open(lock_file, 'w')
+ my_lock.trylock()
+ my_lock.unlock()
+ os._exit(1)
+ except IOError:
+ os._exit(0)
+
+
+def lock_files(lock_path, handles_dir, num_handles=50):
+ with pl.InterProcessLock(lock_path):
+
+ # Open some files we can use for locking
+ handles = []
+ for n in range(num_handles):
+ path = os.path.join(handles_dir, ('file-%s' % n))
+ handles.append(open(path, 'w'))
+
+ # Loop over all the handles and try locking the file
+ # without blocking, keep a count of how many files we
+ # were able to lock and then unlock. If the lock fails
+ # we get an IOError and bail out with bad exit code
+ count = 0
+ for handle in handles:
+ try:
+ pl.InterProcessLock._trylock(handle)
+ count += 1
+ pl.InterProcessLock._unlock(handle)
+ except IOError:
+ os._exit(2)
+ finally:
+ handle.close()
+
+ # Check if we were able to open all files
+ if count != num_handles:
+ raise AssertionError("Unable to open all handles")
+
+
+def inter_processlock_helper(lockname, lock_filename, pipe):
+ lock2 = pl.InterProcessLock(lockname)
+ lock2.lockfile = open(lock_filename, 'w')
+ have_lock = False
+ while not have_lock:
+ try:
+ lock2.trylock()
+ have_lock = True
+ except IOError:
+ pass
+ # Hold the lock and wait for the parent
+ pipe.send(None)
+ pipe.recv()
+
+
class ProcessLockTest(test.TestCase):
def setUp(self):
super(ProcessLockTest, self).setUp()
@@ -59,27 +142,13 @@
lock_file = os.path.join(self.lock_dir, 'lock')
lock = pl.InterProcessLock(lock_file)
- def try_lock():
- try:
- my_lock = pl.InterProcessLock(lock_file)
- my_lock.lockfile = open(lock_file, 'w')
- my_lock.trylock()
- my_lock.unlock()
- os._exit(1)
- except IOError:
- os._exit(0)
-
def attempt_acquire(count):
- children = []
- for i in range(count):
- child = multiprocessing.Process(target=try_lock)
- child.start()
- children.append(child)
- exit_codes = []
- for child in children:
- child.join()
- exit_codes.append(child.exitcode)
- return sum(exit_codes)
+ children = [
+ multiprocessing.Process(target=try_lock, args=(lock_file,))
+ for i in range(count)]
+ with scoped_child_processes(children, timeout=10, exitcode=None):
+ pass
+ return sum(c.exitcode for c in children)
self.assertTrue(lock.acquire())
try:
@@ -108,49 +177,17 @@
def _do_test_lock_externally(self, lock_dir):
lock_path = os.path.join(lock_dir, "lock")
- def lock_files(handles_dir):
- with pl.InterProcessLock(lock_path):
-
- # Open some files we can use for locking
- handles = []
- for n in range(50):
- path = os.path.join(handles_dir, ('file-%s' % n))
- handles.append(open(path, 'w'))
-
- # Loop over all the handles and try locking the file
- # without blocking, keep a count of how many files we
- # were able to lock and then unlock. If the lock fails
- # we get an IOError and bail out with bad exit code
- count = 0
- for handle in handles:
- try:
- fcntl.flock(handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
- count += 1
- fcntl.flock(handle, fcntl.LOCK_UN)
- except IOError:
- os._exit(2)
- finally:
- handle.close()
-
- # Check if we were able to open all files
- self.assertEqual(50, count)
-
handles_dir = tempfile.mkdtemp()
self.tmp_dirs.append(handles_dir)
- children = []
- for n in range(50):
- pid = os.fork()
- if pid:
- children.append(pid)
- else:
- try:
- lock_files(handles_dir)
- finally:
- os._exit(0)
- for child in children:
- (pid, status) = os.waitpid(child, 0)
- if pid:
- self.assertEqual(0, status)
+
+ num_handles = 50
+ num_processes = 50
+ args = [lock_path, handles_dir, num_handles]
+ children = [multiprocessing.Process(target=lock_files, args=args)
+ for _ in range(num_processes)]
+
+ with scoped_child_processes(children, timeout=30, exitcode=0):
+ pass
def test_lock_externally(self):
self._do_test_lock_externally(self.lock_dir)
@@ -180,16 +217,20 @@
def test_interprocess_lock(self):
lock_file = os.path.join(self.lock_dir, 'lock')
+ lock_name = 'foo'
+
+ child_pipe, them = multiprocessing.Pipe()
+ child = multiprocessing.Process(
+ target=inter_processlock_helper, args=(lock_name, lock_file, them))
+
+ with scoped_child_processes((child,)):
- pid = os.fork()
- if pid:
# Make sure the child grabs the lock first
+ if not child_pipe.poll(5):
+ self.fail('Timed out waiting for child to grab lock')
+
start = time.time()
- while not os.path.exists(lock_file):
- if time.time() - start > 5:
- self.fail('Timed out waiting for child to grab lock')
- time.sleep(0)
- lock1 = pl.InterProcessLock('foo')
+ lock1 = pl.InterProcessLock(lock_name)
lock1.lockfile = open(lock_file, 'w')
# NOTE(bnemec): There is a brief window between when the lock file
# is created and when it actually becomes locked. If we happen to
@@ -206,26 +247,10 @@
break
else:
self.fail('Never caught expected lock exception')
- # We don't need to wait for the full sleep in the child here
- os.kill(pid, signal.SIGKILL)
- else:
- try:
- lock2 = pl.InterProcessLock('foo')
- lock2.lockfile = open(lock_file, 'w')
- have_lock = False
- while not have_lock:
- try:
- lock2.trylock()
- have_lock = True
- except IOError:
- pass
- finally:
- # NOTE(bnemec): This is racy, but I don't want to add any
- # synchronization primitives that might mask a problem
- # with the one we're trying to test here.
- time.sleep(.5)
- os._exit(0)
+ child_pipe.send(None)
+
+ @test.testtools.skipIf(WIN32, "Windows cannot open file handles twice")
def test_non_destructive(self):
lock_file = os.path.join(self.lock_dir, 'not-destroyed')
with open(lock_file, 'w') as f:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/fasteners-0.14.1/fasteners.egg-info/PKG-INFO
new/fasteners-0.15/fasteners.egg-info/PKG-INFO
--- old/fasteners-0.14.1/fasteners.egg-info/PKG-INFO 2015-11-13
07:47:25.000000000 +0100
+++ new/fasteners-0.15/fasteners.egg-info/PKG-INFO 2019-05-20
19:36:52.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: fasteners
-Version: 0.14.1
+Version: 0.15
Summary: A python package that provides useful locks.
Home-page: https://github.com/harlowja/fasteners
Author: Joshua Harlow
@@ -12,6 +12,9 @@
.. image:: https://travis-ci.org/harlowja/fasteners.png?branch=master
:target: https://travis-ci.org/harlowja/fasteners
+ .. image:: https://ci.appveyor.com/api/projects/status/7d7aku32pimpadiv
+ :target: https://ci.appveyor.com/project/JoshuaHarlow/fasteners
+
.. image::
https://readthedocs.org/projects/fasteners/badge/?version=latest
:target: https://readthedocs.org/projects/fasteners/?badge=latest
:alt: Documentation Status
@@ -71,7 +74,7 @@
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/fasteners-0.14.1/setup.cfg
new/fasteners-0.15/setup.cfg
--- old/fasteners-0.14.1/setup.cfg 2015-11-13 07:47:25.000000000 +0100
+++ new/fasteners-0.15/setup.cfg 2019-05-20 19:36:52.000000000 +0200
@@ -4,5 +4,4 @@
[egg_info]
tag_build =
tag_date = 0
-tag_svn_revision = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/fasteners-0.14.1/setup.py new/fasteners-0.15/setup.py
--- old/fasteners-0.14.1/setup.py 2015-11-13 07:46:46.000000000 +0100
+++ new/fasteners-0.15/setup.py 2019-05-20 19:35:56.000000000 +0200
@@ -31,7 +31,7 @@
setup(
name='fasteners',
- version='0.14.1',
+ version='0.15',
description='A python package that provides useful locks.',
author="Joshua Harlow",
author_email='[email protected]',
@@ -45,10 +45,10 @@
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
],
keywords="locks thread threads interprocess"
" processes process fasteners",