comparison env/lib/python3.7/site-packages/cachecontrol/caches/file_cache.py @ 5:9b1c78e6ba9c draft default tip

"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author shellac
date Mon, 01 Jun 2020 08:59:25 -0400
parents 79f47841a781
children
comparison
equal deleted inserted replaced
4:79f47841a781 5:9b1c78e6ba9c
1 import hashlib
2 import os
3
4 from lockfile import LockFile
5 from lockfile.mkdirlockfile import MkdirLockFile
6
7 from ..cache import BaseCache
8 from ..controller import CacheController
9
10
11 def _secure_open_write(filename, fmode):
12 # We only want to write to this file, so open it in write only mode
13 flags = os.O_WRONLY
14
15 # os.O_CREAT | os.O_EXCL will fail if the file already exists, so we only
16 # will open *new* files.
17 # We specify this because we want to ensure that the mode we pass is the
18 # mode of the file.
19 flags |= os.O_CREAT | os.O_EXCL
20
21 # Do not follow symlinks to prevent someone from making a symlink that
22 # we follow and insecurely open a cache file.
23 if hasattr(os, "O_NOFOLLOW"):
24 flags |= os.O_NOFOLLOW
25
26 # On Windows we'll mark this file as binary
27 if hasattr(os, "O_BINARY"):
28 flags |= os.O_BINARY
29
30 # Before we open our file, we want to delete any existing file that is
31 # there
32 try:
33 os.remove(filename)
34 except (IOError, OSError):
35 # The file must not exist already, so we can just skip ahead to opening
36 pass
37
38 # Open our file, the use of os.O_CREAT | os.O_EXCL will ensure that if a
39 # race condition happens between the os.remove and this line, that an
40 # error will be raised. Because we utilize a lockfile this should only
41 # happen if someone is attempting to attack us.
42 fd = os.open(filename, flags, fmode)
43 try:
44 return os.fdopen(fd, "wb")
45 except:
46 # An error occurred wrapping our FD in a file object
47 os.close(fd)
48 raise
49
50
51 class FileCache(BaseCache):
52 def __init__(self, directory, forever=False, filemode=0o0600,
53 dirmode=0o0700, use_dir_lock=None, lock_class=None):
54
55 if use_dir_lock is not None and lock_class is not None:
56 raise ValueError("Cannot use use_dir_lock and lock_class together")
57
58 if use_dir_lock:
59 lock_class = MkdirLockFile
60
61 if lock_class is None:
62 lock_class = LockFile
63
64 self.directory = directory
65 self.forever = forever
66 self.filemode = filemode
67 self.dirmode = dirmode
68 self.lock_class = lock_class
69
70
71 @staticmethod
72 def encode(x):
73 return hashlib.sha224(x.encode()).hexdigest()
74
75 def _fn(self, name):
76 # NOTE: This method should not change as some may depend on it.
77 # See: https://github.com/ionrock/cachecontrol/issues/63
78 hashed = self.encode(name)
79 parts = list(hashed[:5]) + [hashed]
80 return os.path.join(self.directory, *parts)
81
82 def get(self, key):
83 name = self._fn(key)
84 if not os.path.exists(name):
85 return None
86
87 with open(name, 'rb') as fh:
88 return fh.read()
89
90 def set(self, key, value):
91 name = self._fn(key)
92
93 # Make sure the directory exists
94 try:
95 os.makedirs(os.path.dirname(name), self.dirmode)
96 except (IOError, OSError):
97 pass
98
99 with self.lock_class(name) as lock:
100 # Write our actual file
101 with _secure_open_write(lock.path, self.filemode) as fh:
102 fh.write(value)
103
104 def delete(self, key):
105 name = self._fn(key)
106 if not self.forever:
107 os.remove(name)
108
109
110 def url_to_file_path(url, filecache):
111 """Return the file cache path based on the URL.
112
113 This does not ensure the file exists!
114 """
115 key = CacheController.cache_url(url)
116 return filecache._fn(key)