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

Side by Side Diff: Lib/dbm/dumb.py

Issue 19385: dbm.dumb should be consistent when the database is closed
Patch Set: Created 6 years 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:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 118
119 sync = _commit 119 sync = _commit
120 120
121 def _verify_open(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._verify_open() 128 try:
129 pos, siz = self._index[key] # may raise KeyError 129 pos, siz = self._index[key] # may raise KeyError
130 except TypeError:
131 raise error('DBM object has already been closed') from None
130 f = _io.open(self._datfile, 'rb') 132 f = _io.open(self._datfile, 'rb')
131 f.seek(pos) 133 f.seek(pos)
132 dat = f.read(siz) 134 dat = f.read(siz)
133 f.close() 135 f.close()
134 return dat 136 return dat
135 137
136 # Append val to the data file, starting at a _BLOCKSIZE-aligned 138 # Append val to the data file, starting at a _BLOCKSIZE-aligned
137 # offset. The data file is first padded with NUL bytes (if needed) 139 # offset. The data file is first padded with NUL bytes (if needed)
138 # to get to an aligned offset. Return pair 140 # to get to an aligned offset. Return pair
139 # (starting offset of val, len(val)) 141 # (starting offset of val, len(val))
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 173
172 def __setitem__(self, key, val): 174 def __setitem__(self, key, val):
173 if isinstance(key, str): 175 if isinstance(key, str):
174 key = key.encode('utf-8') 176 key = key.encode('utf-8')
175 elif not isinstance(key, (bytes, bytearray)): 177 elif not isinstance(key, (bytes, bytearray)):
176 raise TypeError("keys must be bytes or strings") 178 raise TypeError("keys must be bytes or strings")
177 if isinstance(val, str): 179 if isinstance(val, str):
178 val = val.encode('utf-8') 180 val = val.encode('utf-8')
179 elif not isinstance(val, (bytes, bytearray)): 181 elif not isinstance(val, (bytes, bytearray)):
180 raise TypeError("values must be bytes or strings") 182 raise TypeError("values must be bytes or strings")
181 self._verify_open() 183
182 if key not in self._index: 184 try:
185 found = key in self._index
186 except TypeError:
187 raise error('DBM object has already been closed') from None
188
189 if not found:
183 self._addkey(key, self._addval(val)) 190 self._addkey(key, self._addval(val))
184 else: 191 else:
185 # See whether the new value is small enough to fit in the 192 # See whether the new value is small enough to fit in the
186 # (padded) space currently occupied by the old value. 193 # (padded) space currently occupied by the old value.
187 pos, siz = self._index[key] 194 pos, siz = self._index[key]
188 oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE 195 oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE
189 newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE 196 newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE
190 if newblocks <= oldblocks: 197 if newblocks <= oldblocks:
191 self._index[key] = self._setval(pos, val) 198 self._index[key] = self._setval(pos, val)
192 else: 199 else:
193 # The new value doesn't fit in the (padded) space used 200 # The new value doesn't fit in the (padded) space used
194 # by the old value. The blocks used by the old value are 201 # by the old value. The blocks used by the old value are
195 # forever lost. 202 # forever lost.
196 self._index[key] = self._addval(val) 203 self._index[key] = self._addval(val)
197 204
198 # Note that _index may be out of synch with the directory 205 # Note that _index may be out of synch with the directory
199 # file now: _setval() and _addval() don't update the directory 206 # file now: _setval() and _addval() don't update the directory
200 # file. This also means that the on-disk directory and data 207 # file. This also means that the on-disk directory and data
201 # files are in a mutually inconsistent state, and they'll 208 # files are in a mutually inconsistent state, and they'll
202 # remain that way until _commit() is called. Note that this 209 # remain that way until _commit() is called. Note that this
203 # is a disaster (for the database) if the program crashes 210 # is a disaster (for the database) if the program crashes
204 # (so that _commit() never gets called). 211 # (so that _commit() never gets called).
205 212
206 def __delitem__(self, key): 213 def __delitem__(self, key):
207 if isinstance(key, str): 214 if isinstance(key, str):
208 key = key.encode('utf-8') 215 key = key.encode('utf-8')
209 self._verify_open()
210 # The blocks used by the associated value are lost. 216 # The blocks used by the associated value are lost.
211 del self._index[key] 217 try:
218 del self._index[key]
219 except TypeError:
220 raise error('DBM object has already been closed') from None
212 # XXX It's unclear why we do a _commit() here (the code always 221 # 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 222 # 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 223 # XXX keep the directory file in synch. Why should we? Or
215 # XXX why shouldn't __setitem__? 224 # XXX why shouldn't __setitem__?
216 self._commit() 225 self._commit()
217 226
218 def keys(self): 227 def keys(self):
219 self._verify_open() 228 self._verify_open()
220 return list(self._index.keys()) 229 return list(self._index.keys())
221 230
222 def items(self): 231 def items(self):
223 self._verify_open() 232 self._verify_open()
224 return [(key, self[key]) for key in self._index.keys()] 233 return [(key, self[key]) for key in self._index.keys()]
225 234
226 def __contains__(self, key): 235 def __contains__(self, key):
227 if isinstance(key, str): 236 if isinstance(key, str):
228 key = key.encode('utf-8') 237 key = key.encode('utf-8')
229 self._verify_open() 238 try:
230 return key in self._index 239 return key in self._index
240 except TypeError:
241 raise error('DBM object has already been closed') from None
231 242
232 def iterkeys(self): 243 def iterkeys(self):
233 self._verify_open() 244 self._verify_open()
234 return iter(self._index.keys()) 245 return iter(self._index.keys())
235 __iter__ = iterkeys 246 __iter__ = iterkeys
236 247
237 def __len__(self): 248 def __len__(self):
238 self._verify_open() 249 try:
239 return len(self._index) 250 return len(self._index)
251 except TypeError:
252 raise error('DBM object has already been closed') from None
240 253
241 def close(self): 254 def close(self):
242 self._commit() 255 self._commit()
243 self._index = self._datfile = self._dirfile = self._bakfile = None 256 self._index = self._datfile = self._dirfile = self._bakfile = None
244 257
245 __del__ = close 258 __del__ = close
246 259
247 def _chmod(self, file): 260 def _chmod(self, file):
248 if hasattr(self._os, 'chmod'): 261 if hasattr(self._os, 'chmod'):
249 self._os.chmod(file, self._mode) 262 self._os.chmod(file, self._mode)
(...skipping 24 matching lines...) Expand all
274 try: 287 try:
275 um = _os.umask(0) 288 um = _os.umask(0)
276 _os.umask(um) 289 _os.umask(um)
277 except AttributeError: 290 except AttributeError:
278 pass 291 pass
279 else: 292 else:
280 # Turn off any bits that are set in the umask 293 # Turn off any bits that are set in the umask
281 mode = mode & (~um) 294 mode = mode & (~um)
282 295
283 return _Database(file, mode) 296 return _Database(file, mode)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

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