1 # dblite.py module contributed by Ralf W. Grosse-Kunstleve.
2 # Extended for Unicode by Steven Knight.
10 keep_all_files = 00000
11 ignore_corrupt_dbfiles = 0
13 def corruption_warning(filename):
14 print "Warning: Discarding corrupt database:", filename
19 return isinstance(s, str)
22 return type(s) in (str, unicode)
27 def unicode(s): return s
29 dblite_suffix = '.dblite'
34 # Squirrel away references to the functions in various modules
35 # that we'll use when our __del__() method calls our sync() method
36 # during shutdown. We might get destroyed when Python is in the midst
37 # of tearing down the different modules we import in an essentially
38 # arbitrary order, and some of the various modules's global attributes
39 # may already be wiped out from under us.
41 # See the discussion at:
42 # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html
44 _open = __builtin__.open
45 _cPickle_dump = cPickle.dump
49 except AttributeError:
51 _os_rename = os.rename
52 _os_unlink = os.unlink
53 _shutil_copyfile = shutil.copyfile
54 _time_time = time.time
56 def __init__(self, file_base_name, flag, mode):
57 assert flag in (None, "r", "w", "c", "n")
58 if (flag is None): flag = "r"
59 base, ext = os.path.splitext(file_base_name)
60 if ext == dblite_suffix:
61 # There's already a suffix on the file name, don't add one.
62 self._file_name = file_base_name
63 self._tmp_name = base + tmp_suffix
65 self._file_name = file_base_name + dblite_suffix
66 self._tmp_name = file_base_name + tmp_suffix
70 self._needs_sync = 00000
71 if self._os_chown is not None and (os.geteuid()==0 or os.getuid()==0):
72 # running as root; chown back to current owner/group when done
74 statinfo = os.stat(self._file_name)
75 self._chown_to = statinfo.st_uid
76 self._chgrp_to = statinfo.st_gid
78 # db file doesn't exist yet.
79 # Check os.environ for SUDO_UID, use if set
80 self._chown_to = int(os.environ.get('SUDO_UID', -1))
81 self._chgrp_to = int(os.environ.get('SUDO_GID', -1))
83 self._chown_to = -1 # don't chown
84 self._chgrp_to = -1 # don't chgrp
85 if (self._flag == "n"):
86 self._open(self._file_name, "wb", self._mode)
89 f = self._open(self._file_name, "rb")
91 if (self._flag != "c"):
93 self._open(self._file_name, "wb", self._mode)
98 self._dict = cPickle.loads(p)
99 except (cPickle.UnpicklingError, EOFError):
100 if (ignore_corrupt_dbfiles == 0): raise
101 if (ignore_corrupt_dbfiles == 1):
102 corruption_warning(self._file_name)
105 if (self._needs_sync):
109 self._check_writable()
110 f = self._open(self._tmp_name, "wb", self._mode)
111 self._cPickle_dump(self._dict, f, 1)
113 # Windows doesn't allow renaming if the file exists, so unlink
114 # it first, chmod'ing it to make sure we can do so. On UNIX, we
115 # may not be able to chmod the file if it's owned by someone else
116 # (e.g. from a previous run as root). We should still be able to
117 # unlink() the file if the directory's writable, though, so ignore
118 # any OSError exception thrown by the chmod() call.
119 try: self._os_chmod(self._file_name, 0777)
121 self._os_unlink(self._file_name)
122 self._os_rename(self._tmp_name, self._file_name)
123 if self._os_chown is not None and self._chown_to > 0: # don't chown to root or -1
125 self._os_chown(self._file_name, self._chown_to, self._chgrp_to)
128 self._needs_sync = 00000
130 self._shutil_copyfile(
132 self._file_name + "_" + str(int(self._time_time())))
134 def _check_writable(self):
135 if (self._flag == "r"):
136 raise IOError("Read-only database: %s" % self._file_name)
138 def __getitem__(self, key):
139 return self._dict[key]
141 def __setitem__(self, key, value):
142 self._check_writable()
143 if (not is_string(key)):
144 raise TypeError, "key `%s' must be a string but is %s" % (key, type(key))
145 if (not is_string(value)):
146 raise TypeError, "value `%s' must be a string but is %s" % (value, type(value))
147 self._dict[key] = value
148 self._needs_sync = 0001
151 return self._dict.keys()
153 def has_key(self, key):
154 return key in self._dict
156 def __contains__(self, key):
157 return key in self._dict
160 return self._dict.iterkeys()
165 return len(self._dict)
167 def open(file, flag=None, mode=0666):
168 return dblite(file, flag, mode)
171 db = open("tmp", "n")
174 assert db["foo"] == "bar"
175 db[unicode("ufoo")] = unicode("ubar")
176 assert db[unicode("ufoo")] == unicode("ubar")
178 db = open("tmp", "c")
179 assert len(db) == 2, len(db)
180 assert db["foo"] == "bar"
182 assert db["bar"] == "foo"
183 db[unicode("ubar")] = unicode("ufoo")
184 assert db[unicode("ubar")] == unicode("ufoo")
186 db = open("tmp", "r")
187 assert len(db) == 4, len(db)
188 assert db["foo"] == "bar"
189 assert db["bar"] == "foo"
190 assert db[unicode("ufoo")] == unicode("ubar")
191 assert db[unicode("ubar")] == unicode("ufoo")
195 assert str(e) == "Read-only database: tmp.dblite"
197 raise RuntimeError, "IOError expected."
198 db = open("tmp", "w")
205 assert str(e) == "key `(1, 2)' must be a string but is <type 'tuple'>", str(e)
207 raise RuntimeError, "TypeError exception expected"
211 assert str(e) == "value `[1, 2]' must be a string but is <type 'list'>", str(e)
213 raise RuntimeError, "TypeError exception expected"
214 db = open("tmp", "r")
216 db = open("tmp", "n")
218 _open("tmp.dblite", "w")
219 db = open("tmp", "r")
220 _open("tmp.dblite", "w").write("x")
222 db = open("tmp", "r")
223 except cPickle.UnpicklingError:
226 raise RuntimeError, "cPickle exception expected."
227 global ignore_corrupt_dbfiles
228 ignore_corrupt_dbfiles = 2
229 db = open("tmp", "r")
231 os.unlink("tmp.dblite")
233 db = open("tmp", "w")
235 assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e)
237 raise RuntimeError, "IOError expected."
240 if (__name__ == "__main__"):
245 # indent-tabs-mode:nil
247 # vim: set expandtab tabstop=4 shiftwidth=4: