Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1)

Delta Between Two Patch Sets: Lib/dbm/dumb.py

Issue 19385: dbm.dumb should be consistent when the database is closed
Left Patch Set: Created 6 years, 3 months ago
Right Patch Set: Created 5 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 """A dumb and slow but simple dbm clone. 1 """A dumb and slow but simple dbm clone.
2 2
3 For database spam, spam.dir contains the index (a text file), 3 For database spam, spam.dir contains the index (a text file),
4 spam.bak *may* contain a backup of the index (also a text file), 4 spam.bak *may* contain a backup of the index (also a text file),
5 while spam.dat contains the data (a binary file). 5 while spam.dat contains the data (a binary file).
6 6
7 XXX TO DO: 7 XXX TO DO:
8 8
9 - seems to contain a bug when updating... 9 - seems to contain a bug when updating...
10 10
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 f = self._io.open(self._dirfile, 'w', encoding="Latin-1") 111 f = self._io.open(self._dirfile, 'w', encoding="Latin-1")
112 self._chmod(self._dirfile) 112 self._chmod(self._dirfile)
113 for key, pos_and_siz_pair in self._index.items(): 113 for key, pos_and_siz_pair in self._index.items():
114 # Use Latin-1 since it has no qualms with any value in any 114 # Use Latin-1 since it has no qualms with any value in any
115 # position; UTF-8, though, does care sometimes. 115 # position; UTF-8, though, does care sometimes.
116 f.write("%r, %r\n" % (key.decode('Latin-1'), pos_and_siz_pair)) 116 f.write("%r, %r\n" % (key.decode('Latin-1'), pos_and_siz_pair))
117 f.close() 117 f.close()
118 118
119 sync = _commit 119 sync = _commit
120 120
121 def _check_closed(self): 121 def _verify_open(self):
122 if self._index is None: 122 if self._index is None:
123 raise error('DBM object has already been closed') 123 raise error('DBM object has already been closed')
124 124
125 def __getitem__(self, key): 125 def __getitem__(self, key):
126 if isinstance(key, str): 126 if isinstance(key, str):
127 key = key.encode('utf-8') 127 key = key.encode('utf-8')
128 self._check_closed() 128 self._verify_open()
129 pos, siz = self._index[key] # may raise KeyError 129 pos, siz = self._index[key] # may raise KeyError
130 f = _io.open(self._datfile, 'rb') 130 f = _io.open(self._datfile, 'rb')
131 f.seek(pos) 131 f.seek(pos)
132 dat = f.read(siz) 132 dat = f.read(siz)
133 f.close() 133 f.close()
134 return dat 134 return dat
135 135
136 # Append val to the data file, starting at a _BLOCKSIZE-aligned 136 # Append val to the data file, starting at a _BLOCKSIZE-aligned
137 # offset. The data file is first padded with NUL bytes (if needed) 137 # offset. The data file is first padded with NUL bytes (if needed)
138 # to get to an aligned offset. Return pair 138 # to get to an aligned offset. Return pair
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 171
172 def __setitem__(self, key, val): 172 def __setitem__(self, key, val):
173 if isinstance(key, str): 173 if isinstance(key, str):
174 key = key.encode('utf-8') 174 key = key.encode('utf-8')
175 elif not isinstance(key, (bytes, bytearray)): 175 elif not isinstance(key, (bytes, bytearray)):
176 raise TypeError("keys must be bytes or strings") 176 raise TypeError("keys must be bytes or strings")
177 if isinstance(val, str): 177 if isinstance(val, str):
178 val = val.encode('utf-8') 178 val = val.encode('utf-8')
179 elif not isinstance(val, (bytes, bytearray)): 179 elif not isinstance(val, (bytes, bytearray)):
180 raise TypeError("values must be bytes or strings") 180 raise TypeError("values must be bytes or strings")
181 self._check_closed() 181 self._verify_open()
182 if key not in self._index: 182 if key not in self._index:
183 self._addkey(key, self._addval(val)) 183 self._addkey(key, self._addval(val))
184 else: 184 else:
185 # See whether the new value is small enough to fit in the 185 # See whether the new value is small enough to fit in the
186 # (padded) space currently occupied by the old value. 186 # (padded) space currently occupied by the old value.
187 pos, siz = self._index[key] 187 pos, siz = self._index[key]
188 oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE 188 oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE
189 newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE 189 newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE
190 if newblocks <= oldblocks: 190 if newblocks <= oldblocks:
191 self._index[key] = self._setval(pos, val) 191 self._index[key] = self._setval(pos, val)
192 else: 192 else:
193 # The new value doesn't fit in the (padded) space used 193 # The new value doesn't fit in the (padded) space used
194 # by the old value. The blocks used by the old value are 194 # by the old value. The blocks used by the old value are
195 # forever lost. 195 # forever lost.
196 self._index[key] = self._addval(val) 196 self._index[key] = self._addval(val)
197 197
198 # Note that _index may be out of synch with the directory 198 # Note that _index may be out of synch with the directory
199 # file now: _setval() and _addval() don't update the directory 199 # file now: _setval() and _addval() don't update the directory
200 # file. This also means that the on-disk directory and data 200 # file. This also means that the on-disk directory and data
201 # files are in a mutually inconsistent state, and they'll 201 # files are in a mutually inconsistent state, and they'll
202 # remain that way until _commit() is called. Note that this 202 # remain that way until _commit() is called. Note that this
203 # is a disaster (for the database) if the program crashes 203 # is a disaster (for the database) if the program crashes
204 # (so that _commit() never gets called). 204 # (so that _commit() never gets called).
205 205
206 def __delitem__(self, key): 206 def __delitem__(self, key):
207 if isinstance(key, str): 207 if isinstance(key, str):
208 key = key.encode('utf-8') 208 key = key.encode('utf-8')
209 self._check_closed() 209 self._verify_open()
210 # The blocks used by the associated value are lost. 210 # The blocks used by the associated value are lost.
211 del self._index[key] 211 del self._index[key]
212 # XXX It's unclear why we do a _commit() here (the code always 212 # XXX It's unclear why we do a _commit() here (the code always
213 # XXX has, so I'm not changing it). __setitem__ doesn't try to 213 # XXX has, so I'm not changing it). __setitem__ doesn't try to
214 # XXX keep the directory file in synch. Why should we? Or 214 # XXX keep the directory file in synch. Why should we? Or
215 # XXX why shouldn't __setitem__? 215 # XXX why shouldn't __setitem__?
216 self._commit() 216 self._commit()
217 217
218 def keys(self): 218 def keys(self):
219 self._check_closed() 219 try:
220 return list(self._index.keys()) 220 return list(self._index)
221 except TypeError:
222 raise error('DBM object has already been closed') from None
221 223
222 def items(self): 224 def items(self):
223 self._check_closed() 225 self._verify_open()
224 return [(key, self[key]) for key in self._index.keys()] 226 return [(key, self[key]) for key in self._index.keys()]
225 227
226 def __contains__(self, key): 228 def __contains__(self, key):
227 if isinstance(key, str): 229 if isinstance(key, str):
228 key = key.encode('utf-8') 230 key = key.encode('utf-8')
229 self._check_closed() 231 try:
230 return key in self._index 232 return key in self._index
233 except TypeError:
234 if self._index is None:
235 raise error('DBM object has already been closed') from None
236 else:
237 raise
231 238
232 def iterkeys(self): 239 def iterkeys(self):
233 self._check_closed() 240 try:
234 return iter(self._index.keys()) 241 return iter(self._index)
242 except TypeError:
243 raise error('DBM object has already been closed') from None
235 __iter__ = iterkeys 244 __iter__ = iterkeys
236 245
237 def __len__(self): 246 def __len__(self):
238 self._check_closed() 247 try:
239 return len(self._index) 248 return len(self._index)
249 except TypeError:
250 raise error('DBM object has already been closed') from None
240 251
241 def close(self): 252 def close(self):
242 self._commit() 253 self._commit()
243 self._index = self._datfile = self._dirfile = self._bakfile = None 254 self._index = self._datfile = self._dirfile = self._bakfile = None
244 255
245 __del__ = close 256 __del__ = close
246 257
247 def _chmod(self, file): 258 def _chmod(self, file):
248 if hasattr(self._os, 'chmod'): 259 if hasattr(self._os, 'chmod'):
249 self._os.chmod(file, self._mode) 260 self._os.chmod(file, self._mode)
261
262 def __enter__(self):
263 return self
264
265 def __exit__(self, *args):
266 self.close()
250 267
251 268
252 def open(file, flag=None, mode=0o666): 269 def open(file, flag=None, mode=0o666):
253 """Open the database file, filename, and return corresponding object. 270 """Open the database file, filename, and return corresponding object.
254 271
255 The flag argument, used to control how the database is opened in the 272 The flag argument, used to control how the database is opened in the
256 other DBM implementations, is ignored in the dbm.dumb module; the 273 other DBM implementations, is ignored in the dbm.dumb module; the
257 database is always opened for update, and will be created if it does 274 database is always opened for update, and will be created if it does
258 not exist. 275 not exist.
259 276
260 The optional mode argument is the UNIX mode of the file, used only when 277 The optional mode argument is the UNIX mode of the file, used only when
261 the database has to be created. It defaults to octal code 0o666 (and 278 the database has to be created. It defaults to octal code 0o666 (and
262 will be modified by the prevailing umask). 279 will be modified by the prevailing umask).
263 280
264 """ 281 """
265 # flag argument is currently ignored 282 # flag argument is currently ignored
266 283
267 # Modify mode depending on the umask 284 # Modify mode depending on the umask
268 try: 285 try:
269 um = _os.umask(0) 286 um = _os.umask(0)
270 _os.umask(um) 287 _os.umask(um)
271 except AttributeError: 288 except AttributeError:
272 pass 289 pass
273 else: 290 else:
274 # Turn off any bits that are set in the umask 291 # Turn off any bits that are set in the umask
275 mode = mode & (~um) 292 mode = mode & (~um)
276 293
277 return _Database(file, mode) 294 return _Database(file, mode)
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+