summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2018-08-14 20:02:29 -0400
committerJason R. Coombs <jaraco@jaraco.com>2018-08-14 20:07:59 -0400
commit47f9f6068ab791a9b602edb41ebf0a7769bd616c (patch)
tree742e3036151cf15acc7bbac41f05a197ef03ec8e
parent157a4e409551f4008e15e38fd3991aadcd8abbcd (diff)
downloadcherrypy-git-47f9f6068ab791a9b602edb41ebf0a7769bd616c.tar.gz
Replace lockfile with zc.lockfile. Ref #1193.
-rw-r--r--cherrypy/lib/lockfile.py143
-rw-r--r--cherrypy/lib/sessions.py18
-rw-r--r--docs/pkg/cherrypy.lib.rst8
-rwxr-xr-xsetup.py2
4 files changed, 15 insertions, 156 deletions
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': [