Merge branch 'main' into 15397-remove-obsolete-apis
[arvados.git] / sdk / python / arvados / cache.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4
5 import errno
6 import hashlib
7 import os
8 import tempfile
9 import time
10
11 class SafeHTTPCache(object):
12     """Thread-safe replacement for httplib2.FileCache"""
13
14     def __init__(self, path=None, max_age=None):
15         self._dir = path
16         if max_age is not None:
17             try:
18                 self._clean(threshold=time.time() - max_age)
19             except:
20                 pass
21
22     def _clean(self, threshold=0):
23         for ent in os.listdir(self._dir):
24             fnm = os.path.join(self._dir, ent)
25             if os.path.isdir(fnm) or not fnm.endswith('.tmp'):
26                 continue
27             stat = os.lstat(fnm)
28             if stat.st_mtime < threshold:
29                 try:
30                     os.unlink(fnm)
31                 except OSError as err:
32                     if err.errno != errno.ENOENT:
33                         raise
34
35     def __str__(self):
36         return self._dir
37
38     def _filename(self, url):
39         return os.path.join(self._dir, hashlib.md5(url.encode('utf-8')).hexdigest()+'.tmp')
40
41     def get(self, url):
42         filename = self._filename(url)
43         try:
44             with open(filename, 'rb') as f:
45                 return f.read()
46         except (IOError, OSError):
47             return None
48
49     def set(self, url, content):
50         try:
51             fd, tempname = tempfile.mkstemp(dir=self._dir)
52         except:
53             return None
54         try:
55             try:
56                 f = os.fdopen(fd, 'wb')
57             except:
58                 os.close(fd)
59                 raise
60             try:
61                 f.write(content)
62             finally:
63                 f.close()
64             os.rename(tempname, self._filename(url))
65             tempname = None
66         finally:
67             if tempname:
68                 os.unlink(tempname)
69
70     def delete(self, url):
71         try:
72             os.unlink(self._filename(url))
73         except OSError as err:
74             if err.errno != errno.ENOENT:
75                 raise