Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions cachecontrol/caches/file_cache.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import hashlib
import os

from lockfile import LockFile
from lockfile.mkdirlockfile import MkdirLockFile
try:
from fasteners import InterProcessLock
HAS_FASTENERS = True
except ImportError:
class InterProcessLock(object):
pass

HAS_FASTENERS = False

try:
from lockfile import LockFile, LockBase as PYLockFileBase
from lockfile.mkdirlockfile import MkdirLockFile
HAS_PYLOCKFILE = True
except ImportError:
class PYLockFileBase(object):
pass

HAS_PYLOCKFILE = False

from ..cache import BaseCache
from ..controller import CacheController
Expand Down Expand Up @@ -56,18 +72,23 @@ def __init__(self, directory, forever=False, filemode=0o0600,
raise ValueError("Cannot use use_dir_lock and lock_class together")

if use_dir_lock:
if not HAS_PYLOCKFILE:
raise ValueError("Cannot use use_dir_lock without pylockfile.")
lock_class = MkdirLockFile

if lock_class is None:
if lock_class is None and HAS_FASTENERS:
lock_class = InterProcessLock
elif lock_class is None and HAS_PYLOCKFILE:
lock_class = LockFile
else:
raise RuntimeError("Cannot find a file locking library.")

self.directory = directory
self.forever = forever
self.filemode = filemode
self.dirmode = dirmode
self.lock_class = lock_class


@staticmethod
def encode(x):
return hashlib.sha224(x.encode()).hexdigest()
Expand All @@ -79,13 +100,21 @@ def _fn(self, name):
parts = list(hashed[:5]) + [hashed]
return os.path.join(self.directory, *parts)

def _lockname(self, name):
if isinstance(self.lock_class, PYLockFileBase):
return self.name
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be 'name', not 'self.name'

else:
return self.name + ".lock"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.


def get(self, key):
name = self._fn(key)
if not os.path.exists(name):
return None

with open(name, 'rb') as fh:
return fh.read()
with self.lock_class(self._lockname(name)):
if not os.path.exists(name):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be refactored as a try/except block, instead of testing for path existence?

return None

with open(name, 'rb') as fh:
return fh.read()

def set(self, key, value):
name = self._fn(key)
Expand All @@ -96,9 +125,9 @@ def set(self, key, value):
except (IOError, OSError):
pass

with self.lock_class(name) as lock:
with self.lock_class(self._lockname(name)):
# Write our actual file
with _secure_open_write(lock.path, self.filemode) as fh:
with _secure_open_write(name, self.filemode) as fh:
fh.write(value)

def delete(self, key):
Expand Down