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