Package pyraf :: Module filecache
[hide private]
[frames] | no frames]

Source Code for Module pyraf.filecache

  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 # confidence high 
 32   
 33  import os, stat, sys, hashlib 
 34   
35 -class FileCache:
36 37 """File cache base class""" 38
39 - def __init__(self, filename):
40 self.filename = filename 41 self.attributes = self._getAttributes() 42 self.newValue()
43 44 # methods that should be supplied in extended class 45
46 - def getValue(self):
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
56 - def updateValue(self):
57 58 """Called when file has changed.""" 59 60 self.value = self._getFileHandle().read()
61 62 # method that may be changed in extended class 63
64 - def newValue(self):
65 66 """Called when file is new. By default same as updateValue.""" 67 68 self.updateValue()
69 70 # basic method to get cached value or to update if needed 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 # update value if file has changed 84 oldattr = self.attributes 85 if oldattr != newattr: 86 if oldattr[1]>newattr[1] or oldattr[2]>newattr[2]: 87 # warning if current file appears older than cached version 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 # internal utility methods 95
96 - def _getFileHandle(self, filename=None):
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
113 - def _getAttributes(self, filename=None):
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 # file attributes are size, creation, and modification times 129 return st[stat.ST_SIZE], st[stat.ST_CTIME], st[stat.ST_MTIME]
130
131 - def _warning(self, msg):
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
140 -class MD5Cache(FileCache):
141 142 """Cached MD5 digest for file contents""" 143
144 - def getValue(self):
145 146 """Return MD5 digest value associated with file.""" 147 148 return self.value
149
150 - def updateValue(self):
151 152 """Called when file has changed.""" 153 154 contents = self._getFileHandle().read() # is unicode str in py3 155 # md5 digest is the value associated with the file 156 h = hashlib.md5() 157 if sys.version_info[0] > 2: # unicode must be encoded to be hashed 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
166 -class FileCacheDict:
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
173 - def __init__(self, FileCacheClass):
174 self.__Class = FileCacheClass 175 self.data = {}
176
177 - def add(self, filename):
178 """Add filename to dictionary. Does not overwrite existing entry.""" 179 abspath = self.abspath(filename) 180 if not self.data.has_key(abspath): 181 self.data[abspath] = self.__Class(abspath)
182
183 - def abspath(self, filename):
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
191 - def __getitem__(self, filename):
192 abspath = self.abspath(filename) 193 return self.data[abspath].get()
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
208 - def __delitem__(self, filename):
209 abspath = self.abspath(filename) 210 del self.data[abspath]
211
212 - def has_key(self, filename):
213 abspath = self.abspath(filename) 214 return self.data.has_key(abspath)
215
216 - def keys(self):
217 return self.data.keys()
218