From 47f9f6068ab791a9b602edb41ebf0a7769bd616c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 14 Aug 2018 20:02:29 -0400 Subject: Replace lockfile with zc.lockfile. Ref #1193. --- cherrypy/lib/lockfile.py | 143 ---------------------------------------------- cherrypy/lib/sessions.py | 18 ++++-- docs/pkg/cherrypy.lib.rst | 8 --- setup.py | 2 + 4 files changed, 15 insertions(+), 156 deletions(-) delete mode 100644 cherrypy/lib/lockfile.py diff --git a/cherrypy/lib/lockfile.py b/cherrypy/lib/lockfile.py deleted file mode 100644 index e1fe0b8c..00000000 --- a/cherrypy/lib/lockfile.py +++ /dev/null @@ -1,143 +0,0 @@ -""" -Platform-independent file locking. Inspired by and modeled after zc.lockfile. -""" - -import os - -try: - import msvcrt -except ImportError: - pass - -try: - import fcntl -except ImportError: - pass - - -class LockError(Exception): - - 'Could not obtain a lock' - - msg = 'Unable to lock %r' - - def __init__(self, path): - super(LockError, self).__init__(self.msg % path) - - -class UnlockError(LockError): - - 'Could not release a lock' - - msg = 'Unable to unlock %r' - - -# first, a default, naive locking implementation -class NaiveLockFile(object): - - """ - A default, naive locking implementation. Always fails if the file - already exists. - """ - - def __init__(self, path): - self.path = path - try: - fd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_EXCL) - except OSError: - raise LockError(self.path) - os.close(fd) - - def release(self): - os.remove(self.path) - - def remove(self): - pass - - -class SystemLockFile(object): - - """ - An abstract base class for platform-specific locking. - """ - - def __init__(self, path): - self.path = path - - try: - # Open lockfile for writing without truncation: - self.fp = open(path, 'r+') - except IOError: - # If the file doesn't exist, IOError is raised; Use a+ instead. - # 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. - self.fp = open(path, 'a+') - - try: - self._lock_file() - except Exception: - self.fp.seek(1) - self.fp.close() - del self.fp - raise - - self.fp.write(' %s\n' % os.getpid()) - self.fp.truncate() - self.fp.flush() - - def release(self): - if not hasattr(self, 'fp'): - return - self._unlock_file() - self.fp.close() - del self.fp - - def remove(self): - """ - Attempt to remove the file - """ - try: - os.remove(self.path) - except Exception: - pass - - def _unlock_file(self): - """Attempt to obtain the lock on self.fp. Raise UnlockError if not - released.""" - - -class WindowsLockFile(SystemLockFile): - - def _lock_file(self): - # Lock just the first byte - try: - msvcrt.locking(self.fp.fileno(), msvcrt.LK_NBLCK, 1) - except IOError: - raise LockError(self.fp.name) - - def _unlock_file(self): - try: - self.fp.seek(0) - msvcrt.locking(self.fp.fileno(), msvcrt.LK_UNLCK, 1) - except IOError: - raise UnlockError(self.fp.name) - - -class UnixLockFile(SystemLockFile): - - def _lock_file(self): - flags = fcntl.LOCK_EX | fcntl.LOCK_NB - try: - fcntl.flock(self.fp.fileno(), flags) - except IOError: - raise LockError(self.fp.name) - - # no need to implement _unlock_file, it will be unlocked on close() - - -LockFile = ( - UnixLockFile if 'fcntl' in globals() else - WindowsLockFile if 'msvcrt' in globals() else - NaiveLockFile -) diff --git a/cherrypy/lib/sessions.py b/cherrypy/lib/sessions.py index 758e9591..5b49ee13 100644 --- a/cherrypy/lib/sessions.py +++ b/cherrypy/lib/sessions.py @@ -109,13 +109,20 @@ import binascii import six from six.moves import cPickle as pickle +import contextlib2 + +import zc.lockfile import cherrypy from cherrypy.lib import httputil -from cherrypy.lib import lockfile from cherrypy.lib import locking from cherrypy.lib import is_iterator + +if six.PY2: + FileNotFoundError = OSError + + missing = object() @@ -553,8 +560,8 @@ class FileSession(Session): checker = locking.LockChecker(self.id, self.lock_timeout) while not checker.expired(): try: - self.lock = lockfile.LockFile(path) - except lockfile.LockError: + self.lock = zc.lockfile.LockFile(path) + except zc.lockfile.LockError: time.sleep(0.1) else: break @@ -564,8 +571,9 @@ class FileSession(Session): def release_lock(self, path=None): """Release the lock on the currently-loaded session data.""" - self.lock.release() - self.lock.remove() + self.lock.close() + with contextlib2.suppress(FileNotFoundError): + os.remove(self.lock._path) self.locked = False def clean_up(self): diff --git a/docs/pkg/cherrypy.lib.rst b/docs/pkg/cherrypy.lib.rst index 49f10d42..8df1261c 100644 --- a/docs/pkg/cherrypy.lib.rst +++ b/docs/pkg/cherrypy.lib.rst @@ -84,14 +84,6 @@ cherrypy.lib.jsontools module :undoc-members: :show-inheritance: -cherrypy.lib.lockfile module ----------------------------- - -.. automodule:: cherrypy.lib.lockfile - :members: - :undoc-members: - :show-inheritance: - cherrypy.lib.locking module --------------------------- diff --git a/setup.py b/setup.py index 79886904..2e6e9d1a 100755 --- a/setup.py +++ b/setup.py @@ -64,6 +64,8 @@ params = dict( 'cheroot>=6.2.4', 'portend>=2.1.1', 'more_itertools', + 'zc.lockfile', + 'contextlib2', ], extras_require={ 'docs': [ -- cgit v1.2.1