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

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 5 years, 9 months ago
Right Patch Set: Created 5 years, 9 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 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 try: 128 self._verify_open()
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
132 f = _io.open(self._datfile, 'rb') 130 f = _io.open(self._datfile, 'rb')
133 f.seek(pos) 131 f.seek(pos)
134 dat = f.read(siz) 132 dat = f.read(siz)
135 f.close() 133 f.close()
136 return dat 134 return dat
137 135
138 # Append val to the data file, starting at a _BLOCKSIZE-aligned 136 # Append val to the data file, starting at a _BLOCKSIZE-aligned
139 # 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)
140 # to get to an aligned offset. Return pair 138 # to get to an aligned offset. Return pair
141 # (starting offset of val, len(val)) 139 # (starting offset of val, len(val))
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 171
174 def __setitem__(self, key, val): 172 def __setitem__(self, key, val):
175 if isinstance(key, str): 173 if isinstance(key, str):
176 key = key.encode('utf-8') 174 key = key.encode('utf-8')
177 elif not isinstance(key, (bytes, bytearray)): 175 elif not isinstance(key, (bytes, bytearray)):
178 raise TypeError("keys must be bytes or strings") 176 raise TypeError("keys must be bytes or strings")
179 if isinstance(val, str): 177 if isinstance(val, str):
180 val = val.encode('utf-8') 178 val = val.encode('utf-8')
181 elif not isinstance(val, (bytes, bytearray)): 179 elif not isinstance(val, (bytes, bytearray)):
182 raise TypeError("values must be bytes or strings") 180 raise TypeError("values must be bytes or strings")
183 181 self._verify_open()
184 try: 182 if key not in self._index:
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:
190 self._addkey(key, self._addval(val)) 183 self._addkey(key, self._addval(val))
191 else: 184 else:
192 # 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
193 # (padded) space currently occupied by the old value. 186 # (padded) space currently occupied by the old value.
194 pos, siz = self._index[key] 187 pos, siz = self._index[key]
195 oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE 188 oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE
196 newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE 189 newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE
197 if newblocks <= oldblocks: 190 if newblocks <= oldblocks:
198 self._index[key] = self._setval(pos, val) 191 self._index[key] = self._setval(pos, val)
199 else: 192 else:
200 # The new value doesn't fit in the (padded) space used 193 # The new value doesn't fit in the (padded) space used
201 # 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
202 # forever lost. 195 # forever lost.
203 self._index[key] = self._addval(val) 196 self._index[key] = self._addval(val)
204 197
205 # Note that _index may be out of synch with the directory 198 # Note that _index may be out of synch with the directory
206 # file now: _setval() and _addval() don't update the directory 199 # file now: _setval() and _addval() don't update the directory
207 # file. This also means that the on-disk directory and data 200 # file. This also means that the on-disk directory and data
208 # files are in a mutually inconsistent state, and they'll 201 # files are in a mutually inconsistent state, and they'll
209 # remain that way until _commit() is called. Note that this 202 # remain that way until _commit() is called. Note that this
210 # is a disaster (for the database) if the program crashes 203 # is a disaster (for the database) if the program crashes
211 # (so that _commit() never gets called). 204 # (so that _commit() never gets called).
212 205
213 def __delitem__(self, key): 206 def __delitem__(self, key):
214 if isinstance(key, str): 207 if isinstance(key, str):
215 key = key.encode('utf-8') 208 key = key.encode('utf-8')
209 self._verify_open()
216 # The blocks used by the associated value are lost. 210 # The blocks used by the associated value are lost.
217 try: 211 del self._index[key]
218 del self._index[key]
219 except TypeError:
220 raise error('DBM object has already been closed') from None
221 # 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
222 # 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
223 # XXX keep the directory file in synch. Why should we? Or 214 # XXX keep the directory file in synch. Why should we? Or
224 # XXX why shouldn't __setitem__? 215 # XXX why shouldn't __setitem__?
225 self._commit() 216 self._commit()
226 217
227 def keys(self): 218 def keys(self):
228 self._verify_open() 219 try:
229 return list(self._index.keys()) 220 return list(self._index)
221 except TypeError:
222 raise error('DBM object has already been closed') from None
230 223
231 def items(self): 224 def items(self):
232 self._verify_open() 225 self._verify_open()
233 return [(key, self[key]) for key in self._index.keys()] 226 return [(key, self[key]) for key in self._index.keys()]
234 227
235 def __contains__(self, key): 228 def __contains__(self, key):
236 if isinstance(key, str): 229 if isinstance(key, str):
237 key = key.encode('utf-8') 230 key = key.encode('utf-8')
238 try: 231 try:
239 return key in self._index 232 return key in self._index
240 except TypeError: 233 except TypeError:
234 if self._index is None:
235 raise error('DBM object has already been closed') from None
236 else:
237 raise
238
239 def iterkeys(self):
240 try:
241 return iter(self._index)
242 except TypeError:
241 raise error('DBM object has already been closed') from None 243 raise error('DBM object has already been closed') from None
242
243 def iterkeys(self):
244 self._verify_open()
245 return iter(self._index.keys())
246 __iter__ = iterkeys 244 __iter__ = iterkeys
247 245
248 def __len__(self): 246 def __len__(self):
249 try: 247 try:
250 return len(self._index) 248 return len(self._index)
251 except TypeError: 249 except TypeError:
252 raise error('DBM object has already been closed') from None 250 raise error('DBM object has already been closed') from None
253 251
254 def close(self): 252 def close(self):
255 self._commit() 253 self._commit()
256 self._index = self._datfile = self._dirfile = self._bakfile = None 254 self._index = self._datfile = self._dirfile = self._bakfile = None
257 255
258 __del__ = close 256 __del__ = close
(...skipping 28 matching lines...) Expand all
287 try: 285 try:
288 um = _os.umask(0) 286 um = _os.umask(0)
289 _os.umask(um) 287 _os.umask(um)
290 except AttributeError: 288 except AttributeError:
291 pass 289 pass
292 else: 290 else:
293 # Turn off any bits that are set in the umask 291 # Turn off any bits that are set in the umask
294 mode = mode & (~um) 292 mode = mode & (~um)
295 293
296 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+