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

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, 4 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:
View unified diff | Download patch
« no previous file with comments | « no previous file | Lib/test/test_dbm_dumb.py » ('j') | Lib/test/test_dbm_dumb.py » ('J')
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 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 110
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
121 def _check_closed(self):
122 if self._index is None:
123 raise error('DBM object has already been closed')
120 124
121 def __getitem__(self, key): 125 def __getitem__(self, key):
122 if isinstance(key, str): 126 if isinstance(key, str):
123 key = key.encode('utf-8') 127 key = key.encode('utf-8')
128 self._check_closed()
124 pos, siz = self._index[key] # may raise KeyError 129 pos, siz = self._index[key] # may raise KeyError
125 f = _io.open(self._datfile, 'rb') 130 f = _io.open(self._datfile, 'rb')
126 f.seek(pos) 131 f.seek(pos)
127 dat = f.read(siz) 132 dat = f.read(siz)
128 f.close() 133 f.close()
129 return dat 134 return dat
130 135
131 # Append val to the data file, starting at a _BLOCKSIZE-aligned 136 # Append val to the data file, starting at a _BLOCKSIZE-aligned
132 # 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)
133 # 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
166 171
167 def __setitem__(self, key, val): 172 def __setitem__(self, key, val):
168 if isinstance(key, str): 173 if isinstance(key, str):
169 key = key.encode('utf-8') 174 key = key.encode('utf-8')
170 elif not isinstance(key, (bytes, bytearray)): 175 elif not isinstance(key, (bytes, bytearray)):
171 raise TypeError("keys must be bytes or strings") 176 raise TypeError("keys must be bytes or strings")
172 if isinstance(val, str): 177 if isinstance(val, str):
173 val = val.encode('utf-8') 178 val = val.encode('utf-8')
174 elif not isinstance(val, (bytes, bytearray)): 179 elif not isinstance(val, (bytes, bytearray)):
175 raise TypeError("values must be bytes or strings") 180 raise TypeError("values must be bytes or strings")
181 self._check_closed()
176 if key not in self._index: 182 if key not in self._index:
177 self._addkey(key, self._addval(val)) 183 self._addkey(key, self._addval(val))
178 else: 184 else:
179 # 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
180 # (padded) space currently occupied by the old value. 186 # (padded) space currently occupied by the old value.
181 pos, siz = self._index[key] 187 pos, siz = self._index[key]
182 oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE 188 oldblocks = (siz + _BLOCKSIZE - 1) // _BLOCKSIZE
183 newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE 189 newblocks = (len(val) + _BLOCKSIZE - 1) // _BLOCKSIZE
184 if newblocks <= oldblocks: 190 if newblocks <= oldblocks:
185 self._index[key] = self._setval(pos, val) 191 self._index[key] = self._setval(pos, val)
186 else: 192 else:
187 # The new value doesn't fit in the (padded) space used 193 # The new value doesn't fit in the (padded) space used
188 # 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
189 # forever lost. 195 # forever lost.
190 self._index[key] = self._addval(val) 196 self._index[key] = self._addval(val)
191 197
192 # Note that _index may be out of synch with the directory 198 # Note that _index may be out of synch with the directory
193 # file now: _setval() and _addval() don't update the directory 199 # file now: _setval() and _addval() don't update the directory
194 # file. This also means that the on-disk directory and data 200 # file. This also means that the on-disk directory and data
195 # files are in a mutually inconsistent state, and they'll 201 # files are in a mutually inconsistent state, and they'll
196 # remain that way until _commit() is called. Note that this 202 # remain that way until _commit() is called. Note that this
197 # is a disaster (for the database) if the program crashes 203 # is a disaster (for the database) if the program crashes
198 # (so that _commit() never gets called). 204 # (so that _commit() never gets called).
199 205
200 def __delitem__(self, key): 206 def __delitem__(self, key):
201 if isinstance(key, str): 207 if isinstance(key, str):
202 key = key.encode('utf-8') 208 key = key.encode('utf-8')
209 self._check_closed()
203 # The blocks used by the associated value are lost. 210 # The blocks used by the associated value are lost.
204 del self._index[key] 211 del self._index[key]
205 # 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
206 # 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
207 # XXX keep the directory file in synch. Why should we? Or 214 # XXX keep the directory file in synch. Why should we? Or
208 # XXX why shouldn't __setitem__? 215 # XXX why shouldn't __setitem__?
209 self._commit() 216 self._commit()
210 217
211 def keys(self): 218 def keys(self):
219 self._check_closed()
212 return list(self._index.keys()) 220 return list(self._index.keys())
213 221
214 def items(self): 222 def items(self):
223 self._check_closed()
215 return [(key, self[key]) for key in self._index.keys()] 224 return [(key, self[key]) for key in self._index.keys()]
216 225
217 def __contains__(self, key): 226 def __contains__(self, key):
218 if isinstance(key, str): 227 if isinstance(key, str):
219 key = key.encode('utf-8') 228 key = key.encode('utf-8')
229 self._check_closed()
220 return key in self._index 230 return key in self._index
221 231
222 def iterkeys(self): 232 def iterkeys(self):
233 self._check_closed()
223 return iter(self._index.keys()) 234 return iter(self._index.keys())
224 __iter__ = iterkeys 235 __iter__ = iterkeys
225 236
226 def __len__(self): 237 def __len__(self):
238 self._check_closed()
227 return len(self._index) 239 return len(self._index)
228 240
229 def close(self): 241 def close(self):
230 self._commit() 242 self._commit()
231 self._index = self._datfile = self._dirfile = self._bakfile = None 243 self._index = self._datfile = self._dirfile = self._bakfile = None
232 244
233 __del__ = close 245 __del__ = close
234 246
235 def _chmod(self, file): 247 def _chmod(self, file):
236 if hasattr(self._os, 'chmod'): 248 if hasattr(self._os, 'chmod'):
(...skipping 19 matching lines...) Expand all
256 try: 268 try:
257 um = _os.umask(0) 269 um = _os.umask(0)
258 _os.umask(um) 270 _os.umask(um)
259 except AttributeError: 271 except AttributeError:
260 pass 272 pass
261 else: 273 else:
262 # Turn off any bits that are set in the umask 274 # Turn off any bits that are set in the umask
263 mode = mode & (~um) 275 mode = mode & (~um)
264 276
265 return _Database(file, mode) 277 return _Database(file, mode)
OLDNEW
« no previous file with comments | « no previous file | Lib/test/test_dbm_dumb.py » ('j') | Lib/test/test_dbm_dumb.py » ('J')

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