1 """filecache.py: In-memory cache for files with automatic update
2
3 FileCache is the base class for objects that get data from files and
4 that need to stay in sync with files in case they change. It tracks the
5 file creation/modification times and size and calls the updateValue()
6 method if the file has gotten out of date. If the file has not previously
7 been accessed, calls the newValue() method (which by default is the same
8 as updateValue).
9
10 Use the get() method to get the value associated with a file. The
11 getValue() method does not check to see if the file has changed and
12 may also be called if that is the desired effect.
13
14 The base implementation of FileCache just stores and returns the file
15 contents as a string. Extensions should implement at a minimum the
16 getValue and updateValue methods.
17
18 MD5Cache is an implementation of a FileCache that returns the MD5 digest
19 value for a file's contents, updating it only if the file has changed.
20
21 FileCacheDict is a dictionary-like class that keeps FileCache objects
22 for a list of filenames. It is instantiated with the *class* (not an
23 instance) of the objects to be created for each entry. New files
24 are added with the add() method, and values are retrieved by
25 index (cachedict[filename]) or using the .get() method.
26
27 $Id: filecache.py 1463 2011-06-24 22:58:30Z stsci_embray $
28
29 R. White, 2000 October 1
30 """
31 from __future__ import division
32
33 import os, stat, sys, hashlib
34
36
37 """File cache base class"""
38
43
44
45
47
48 """Get info associated with file.
49
50 Usually this is not called directly by the user (use the
51 get() method instead.)
52 """
53
54 return self.value
55
61
62
63
65
66 """Called when file is new. By default same as updateValue."""
67
68 self.updateValue()
69
70
71
72 - def get(self, update=1):
73
74 """Get info associated with file.
75
76 Updates cache if needed, then calls getValue. If the
77 update flag is false, simply returns the value without
78 checking to see if it is out-of-date.
79 """
80
81 if update:
82 newattr = self._getAttributes()
83
84 oldattr = self.attributes
85 if oldattr != newattr:
86 if oldattr[1]>newattr[1] or oldattr[2]>newattr[2]:
87
88 self._warning("Warning: current version of file %s"
89 " is older than cached version" % self.filename)
90 self.updateValue()
91 self.attributes = newattr
92 return self.getValue()
93
94
95
97
98 """Get file handle for a filename or filehandle instance"""
99
100 if filename==None:
101 filename = self.filename
102 if isinstance(filename,str):
103 fh = open(filename, 'r')
104 elif hasattr(filename, 'read'):
105 fh = filename
106 if hasattr(filename, 'seek'):
107 fh.seek(0)
108 else:
109 raise TypeError(
110 "Argument to _getFileHandle must be name or file handle")
111 return fh
112
114
115 """Get file attributes for a file or filehandle"""
116
117 if filename is None:
118 filename = self.filename
119 if not filename:
120 return None
121 elif isinstance(filename,str):
122 st = os.stat(filename)
123 elif hasattr(filename, 'fileno') and hasattr(filename, 'name'):
124 fh = filename
125 st = os.fstat(fh.fileno())
126 else:
127 return None
128
129 return st[stat.ST_SIZE], st[stat.ST_CTIME], st[stat.ST_MTIME]
130
132
133 """Print warning message to stderr, using verbose flag"""
134
135 sys.stdout.flush()
136 sys.stderr.write(msg + "\n")
137 sys.stderr.flush()
138
139
141
142 """Cached MD5 digest for file contents"""
143
145
146 """Return MD5 digest value associated with file."""
147
148 return self.value
149
151
152 """Called when file has changed."""
153
154 contents = self._getFileHandle().read()
155
156 h = hashlib.md5()
157 if sys.version_info[0] > 2:
158 h.update(contents.encode('utf-8'))
159 self.value = str(h.digest())
160 else:
161 h.update(contents)
162 self.value = h.digest()
163
164
165
167
168 """Dictionary-like set of cached values for a set of files
169
170 Initialize with class to be instantiated for each file
171 """
172
174 self.__Class = FileCacheClass
175 self.data = {}
176
177 - def add(self, filename):
182
184 if isinstance(filename,str):
185 return os.path.abspath(filename)
186 elif hasattr(filename, 'name') and hasattr(filename, 'read'):
187 return os.path.abspath(filename.name)
188 else:
189 return filename
190
194
195 - def get(self, filename, update=1):
196 """Get value; add it if filename is not already in cache
197
198 Note that this behavior differs from the usual dictionary
199 get() method -- effectively it never fails.
200 """
201 abspath = self.abspath(filename)
202 obj = self.data.get(abspath)
203 if obj is None:
204 self.add(abspath)
205 obj = self.data[abspath]
206 return obj.get(update=update)
207
211
215
217 return self.data.keys()
218