Package SCons :: Module dblite
[hide private]
[frames] | no frames]

Source Code for Module SCons.dblite

  1  # dblite.py module contributed by Ralf W. Grosse-Kunstleve. 
  2  # Extended for Unicode by Steven Knight. 
  3   
  4  import SCons.compat 
  5   
  6  import os 
  7  # compat layer imports "cPickle" for us if it's available. 
  8  import pickle 
  9  import shutil 
 10  import time 
 11   
 12  keep_all_files = 00000 
 13  ignore_corrupt_dbfiles = 0 
 14   
15 -def corruption_warning(filename):
16 print "Warning: Discarding corrupt database:", filename
17 18 try: unicode 19 except NameError:
20 - def is_string(s):
21 return isinstance(s, str)
22 else:
23 - def is_string(s):
24 return type(s) in (str, unicode)
25 26 try: 27 unicode('a') 28 except NameError:
29 - def unicode(s): return s
30 31 dblite_suffix = '.dblite' 32 tmp_suffix = '.tmp' 33
34 -class dblite(object):
35 36 # Squirrel away references to the functions in various modules 37 # that we'll use when our __del__() method calls our sync() method 38 # during shutdown. We might get destroyed when Python is in the midst 39 # of tearing down the different modules we import in an essentially 40 # arbitrary order, and some of the various modules's global attributes 41 # may already be wiped out from under us. 42 # 43 # See the discussion at: 44 # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html 45 46 _open = open 47 _pickle_dump = staticmethod(pickle.dump) 48 _os_chmod = os.chmod 49 try: 50 _os_chown = os.chown 51 except AttributeError: 52 _os_chown = None 53 _os_rename = os.rename 54 _os_unlink = os.unlink 55 _shutil_copyfile = shutil.copyfile 56 _time_time = time.time 57
58 - def __init__(self, file_base_name, flag, mode):
59 assert flag in (None, "r", "w", "c", "n") 60 if (flag is None): flag = "r" 61 base, ext = os.path.splitext(file_base_name) 62 if ext == dblite_suffix: 63 # There's already a suffix on the file name, don't add one. 64 self._file_name = file_base_name 65 self._tmp_name = base + tmp_suffix 66 else: 67 self._file_name = file_base_name + dblite_suffix 68 self._tmp_name = file_base_name + tmp_suffix 69 self._flag = flag 70 self._mode = mode 71 self._dict = {} 72 self._needs_sync = 00000 73 if self._os_chown is not None and (os.geteuid()==0 or os.getuid()==0): 74 # running as root; chown back to current owner/group when done 75 try: 76 statinfo = os.stat(self._file_name) 77 self._chown_to = statinfo.st_uid 78 self._chgrp_to = statinfo.st_gid 79 except OSError, e: 80 # db file doesn't exist yet. 81 # Check os.environ for SUDO_UID, use if set 82 self._chown_to = int(os.environ.get('SUDO_UID', -1)) 83 self._chgrp_to = int(os.environ.get('SUDO_GID', -1)) 84 else: 85 self._chown_to = -1 # don't chown 86 self._chgrp_to = -1 # don't chgrp 87 if (self._flag == "n"): 88 self._open(self._file_name, "wb", self._mode) 89 else: 90 try: 91 f = self._open(self._file_name, "rb") 92 except IOError, e: 93 if (self._flag != "c"): 94 raise e 95 self._open(self._file_name, "wb", self._mode) 96 else: 97 p = f.read() 98 if (len(p) > 0): 99 try: 100 self._dict = pickle.loads(p) 101 except (pickle.UnpicklingError, EOFError, KeyError): 102 # Note how we catch KeyErrors too here, which might happen 103 # when we don't have cPickle available (default pickle 104 # throws it). 105 if (ignore_corrupt_dbfiles == 0): raise 106 if (ignore_corrupt_dbfiles == 1): 107 corruption_warning(self._file_name)
108
109 - def close(self):
110 if (self._needs_sync): 111 self.sync()
112
113 - def __del__(self):
114 self.close()
115
116 - def sync(self):
117 self._check_writable() 118 f = self._open(self._tmp_name, "wb", self._mode) 119 self._pickle_dump(self._dict, f, 1) 120 f.close() 121 # Windows doesn't allow renaming if the file exists, so unlink 122 # it first, chmod'ing it to make sure we can do so. On UNIX, we 123 # may not be able to chmod the file if it's owned by someone else 124 # (e.g. from a previous run as root). We should still be able to 125 # unlink() the file if the directory's writable, though, so ignore 126 # any OSError exception thrown by the chmod() call. 127 try: self._os_chmod(self._file_name, 0777) 128 except OSError: pass 129 self._os_unlink(self._file_name) 130 self._os_rename(self._tmp_name, self._file_name) 131 if self._os_chown is not None and self._chown_to > 0: # don't chown to root or -1 132 try: 133 self._os_chown(self._file_name, self._chown_to, self._chgrp_to) 134 except OSError: 135 pass 136 self._needs_sync = 00000 137 if (keep_all_files): 138 self._shutil_copyfile( 139 self._file_name, 140 self._file_name + "_" + str(int(self._time_time())))
141
142 - def _check_writable(self):
143 if (self._flag == "r"): 144 raise IOError("Read-only database: %s" % self._file_name)
145
146 - def __getitem__(self, key):
147 return self._dict[key]
148
149 - def __setitem__(self, key, value):
150 self._check_writable() 151 if (not is_string(key)): 152 raise TypeError("key `%s' must be a string but is %s" % (key, type(key))) 153 if (not is_string(value)): 154 raise TypeError("value `%s' must be a string but is %s" % (value, type(value))) 155 self._dict[key] = value 156 self._needs_sync = 0001
157
158 - def keys(self):
159 return list(self._dict.keys())
160
161 - def has_key(self, key):
162 return key in self._dict
163
164 - def __contains__(self, key):
165 return key in self._dict
166
167 - def iterkeys(self):
168 # Wrapping name in () prevents fixer from "fixing" this 169 return (self._dict.iterkeys)()
170 171 __iter__ = iterkeys 172
173 - def __len__(self):
174 return len(self._dict)
175
176 -def open(file, flag=None, mode=0666):
177 return dblite(file, flag, mode)
178
179 -def _exercise():
180 db = open("tmp", "n") 181 assert len(db) == 0 182 db["foo"] = "bar" 183 assert db["foo"] == "bar" 184 db[unicode("ufoo")] = unicode("ubar") 185 assert db[unicode("ufoo")] == unicode("ubar") 186 db.sync() 187 db = open("tmp", "c") 188 assert len(db) == 2, len(db) 189 assert db["foo"] == "bar" 190 db["bar"] = "foo" 191 assert db["bar"] == "foo" 192 db[unicode("ubar")] = unicode("ufoo") 193 assert db[unicode("ubar")] == unicode("ufoo") 194 db.sync() 195 db = open("tmp", "r") 196 assert len(db) == 4, len(db) 197 assert db["foo"] == "bar" 198 assert db["bar"] == "foo" 199 assert db[unicode("ufoo")] == unicode("ubar") 200 assert db[unicode("ubar")] == unicode("ufoo") 201 try: 202 db.sync() 203 except IOError, e: 204 assert str(e) == "Read-only database: tmp.dblite" 205 else: 206 raise RuntimeError("IOError expected.") 207 db = open("tmp", "w") 208 assert len(db) == 4 209 db["ping"] = "pong" 210 db.sync() 211 try: 212 db[(1,2)] = "tuple" 213 except TypeError, e: 214 assert str(e) == "key `(1, 2)' must be a string but is <type 'tuple'>", str(e) 215 else: 216 raise RuntimeError("TypeError exception expected") 217 try: 218 db["list"] = [1,2] 219 except TypeError, e: 220 assert str(e) == "value `[1, 2]' must be a string but is <type 'list'>", str(e) 221 else: 222 raise RuntimeError("TypeError exception expected") 223 db = open("tmp", "r") 224 assert len(db) == 5 225 db = open("tmp", "n") 226 assert len(db) == 0 227 dblite._open("tmp.dblite", "w") 228 db = open("tmp", "r") 229 dblite._open("tmp.dblite", "w").write("x") 230 try: 231 db = open("tmp", "r") 232 except pickle.UnpicklingError: 233 pass 234 else: 235 raise RuntimeError("pickle exception expected.") 236 global ignore_corrupt_dbfiles 237 ignore_corrupt_dbfiles = 2 238 db = open("tmp", "r") 239 assert len(db) == 0 240 os.unlink("tmp.dblite") 241 try: 242 db = open("tmp", "w") 243 except IOError, e: 244 assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e) 245 else: 246 raise RuntimeError("IOError expected.") 247 print "OK"
248 249 if (__name__ == "__main__"): 250 _exercise() 251 252 # Local Variables: 253 # tab-width:4 254 # indent-tabs-mode:nil 255 # End: 256 # vim: set expandtab tabstop=4 shiftwidth=4: 257