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

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, 11 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 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)
(...skipping 24 matching lines...) Expand all
274 try: 285 try:
275 um = _os.umask(0) 286 um = _os.umask(0)
276 _os.umask(um) 287 _os.umask(um)
277 except AttributeError: 288 except AttributeError:
278 pass 289 pass
279 else: 290 else:
280 # Turn off any bits that are set in the umask 291 # Turn off any bits that are set in the umask
281 mode = mode & (~um) 292 mode = mode & (~um)
282 293
283 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+