1
2
3 from __future__ import print_function
4
5 import os
6 import pickle
7 import shutil
8 import time
9
10 from SCons.compat import PICKLE_PROTOCOL
11
12 keep_all_files = 00000
13 ignore_corrupt_dbfiles = 0
14
15
17 print("Warning: Discarding corrupt database:", filename)
18
19
20 try:
21 unicode
22 except NameError:
24 return isinstance(s, str)
25 else:
28
29
31 return isinstance(s, bytes)
32
33
34 try:
35 unicode('a')
36 except NameError:
39
40 dblite_suffix = '.dblite'
41
42
43
44
45 tmp_suffix = '.tmp'
46
47
49 """
50 Squirrel away references to the functions in various modules
51 that we'll use when our __del__() method calls our sync() method
52 during shutdown. We might get destroyed when Python is in the midst
53 of tearing down the different modules we import in an essentially
54 arbitrary order, and some of the various modules's global attributes
55 may already be wiped out from under us.
56
57 See the discussion at:
58 http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html
59 """
60
61 _open = open
62 _pickle_dump = staticmethod(pickle.dump)
63 _pickle_protocol = PICKLE_PROTOCOL
64 _os_chmod = os.chmod
65
66 try:
67 _os_chown = os.chown
68 except AttributeError:
69 _os_chown = None
70
71 _os_rename = os.rename
72 _os_unlink = os.unlink
73 _shutil_copyfile = shutil.copyfile
74 _time_time = time.time
75
76 - def __init__(self, file_base_name, flag, mode):
77 assert flag in (None, "r", "w", "c", "n")
78 if flag is None:
79 flag = "r"
80
81 base, ext = os.path.splitext(file_base_name)
82 if ext == dblite_suffix:
83
84 self._file_name = file_base_name
85 self._tmp_name = base + tmp_suffix
86 else:
87 self._file_name = file_base_name + dblite_suffix
88 self._tmp_name = file_base_name + tmp_suffix
89
90 self._flag = flag
91 self._mode = mode
92 self._dict = {}
93 self._needs_sync = 00000
94
95 if self._os_chown is not None and (os.geteuid() == 0 or os.getuid() == 0):
96
97 try:
98 statinfo = os.stat(self._file_name)
99 self._chown_to = statinfo.st_uid
100 self._chgrp_to = statinfo.st_gid
101 except OSError as e:
102
103
104 self._chown_to = int(os.environ.get('SUDO_UID', -1))
105 self._chgrp_to = int(os.environ.get('SUDO_GID', -1))
106 else:
107 self._chown_to = -1
108 self._chgrp_to = -1
109
110 if self._flag == "n":
111 self._open(self._file_name, "wb", self._mode)
112 else:
113 try:
114 f = self._open(self._file_name, "rb")
115 except IOError as e:
116 if self._flag != "c":
117 raise e
118 self._open(self._file_name, "wb", self._mode)
119 else:
120 p = f.read()
121 if len(p) > 0:
122 try:
123 if bytes is not str:
124 self._dict = pickle.loads(p, encoding='bytes')
125 else:
126 self._dict = pickle.loads(p)
127 except (pickle.UnpicklingError, EOFError, KeyError):
128
129
130
131 if (ignore_corrupt_dbfiles == 0): raise
132 if (ignore_corrupt_dbfiles == 1):
133 corruption_warning(self._file_name)
134
136 if self._needs_sync:
137 self.sync()
138
141
143 self._check_writable()
144 f = self._open(self._tmp_name, "wb", self._mode)
145 self._pickle_dump(self._dict, f, self._pickle_protocol)
146 f.close()
147
148
149
150
151
152
153
154 try:
155 self._os_chmod(self._file_name, 0o777)
156 except OSError:
157 pass
158 self._os_unlink(self._file_name)
159 self._os_rename(self._tmp_name, self._file_name)
160 if self._os_chown is not None and self._chown_to > 0:
161 try:
162 self._os_chown(self._file_name, self._chown_to, self._chgrp_to)
163 except OSError:
164 pass
165 self._needs_sync = 00000
166 if (keep_all_files):
167 self._shutil_copyfile(
168 self._file_name,
169 self._file_name + "_" + str(int(self._time_time())))
170
172 if (self._flag == "r"):
173 raise IOError("Read-only database: %s" % self._file_name)
174
176 return self._dict[key]
177
179 self._check_writable()
180 if (not is_string(key)):
181 raise TypeError("key `%s' must be a string but is %s" % (key, type(key)))
182 if (not is_bytes(value)):
183 raise TypeError("value `%s' must be a bytes but is %s" % (value, type(value)))
184 self._dict[key] = value
185 self._needs_sync = 0o001
186
188 return list(self._dict.keys())
189
191 return key in self._dict
192
194 return key in self._dict
195
199
200 __iter__ = iterkeys
201
203 return len(self._dict)
204
205
206 -def open(file, flag=None, mode=0o666):
208
209
211 db = open("tmp", "n")
212 assert len(db) == 0
213 db["foo"] = "bar"
214 assert db["foo"] == "bar"
215 db[unicode("ufoo")] = unicode("ubar")
216 assert db[unicode("ufoo")] == unicode("ubar")
217 db.sync()
218 db = open("tmp", "c")
219 assert len(db) == 2, len(db)
220 assert db["foo"] == "bar"
221 db["bar"] = "foo"
222 assert db["bar"] == "foo"
223 db[unicode("ubar")] = unicode("ufoo")
224 assert db[unicode("ubar")] == unicode("ufoo")
225 db.sync()
226 db = open("tmp", "r")
227 assert len(db) == 4, len(db)
228 assert db["foo"] == "bar"
229 assert db["bar"] == "foo"
230 assert db[unicode("ufoo")] == unicode("ubar")
231 assert db[unicode("ubar")] == unicode("ufoo")
232 try:
233 db.sync()
234 except IOError as e:
235 assert str(e) == "Read-only database: tmp.dblite"
236 else:
237 raise RuntimeError("IOError expected.")
238 db = open("tmp", "w")
239 assert len(db) == 4
240 db["ping"] = "pong"
241 db.sync()
242 try:
243 db[(1, 2)] = "tuple"
244 except TypeError as e:
245 assert str(e) == "key `(1, 2)' must be a string but is <type 'tuple'>", str(e)
246 else:
247 raise RuntimeError("TypeError exception expected")
248 try:
249 db["list"] = [1, 2]
250 except TypeError as e:
251 assert str(e) == "value `[1, 2]' must be a string but is <type 'list'>", str(e)
252 else:
253 raise RuntimeError("TypeError exception expected")
254 db = open("tmp", "r")
255 assert len(db) == 5
256 db = open("tmp", "n")
257 assert len(db) == 0
258 dblite._open("tmp.dblite", "w")
259 db = open("tmp", "r")
260 dblite._open("tmp.dblite", "w").write("x")
261 try:
262 db = open("tmp", "r")
263 except pickle.UnpicklingError:
264 pass
265 else:
266 raise RuntimeError("pickle exception expected.")
267 global ignore_corrupt_dbfiles
268 ignore_corrupt_dbfiles = 2
269 db = open("tmp", "r")
270 assert len(db) == 0
271 os.unlink("tmp.dblite")
272 try:
273 db = open("tmp", "w")
274 except IOError as e:
275 assert str(e) == "[Errno 2] No such file or directory: 'tmp.dblite'", str(e)
276 else:
277 raise RuntimeError("IOError expected.")
278
279
280 if (__name__ == "__main__"):
281 _exercise()
282
283
284
285
286
287
288