blob: 8264f2d8121aa0a656d43bd2891862257601e27f [file] [log] [blame]
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +00001#!/bin/env python
2#------------------------------------------------------------------------
3# Copyright (c) 1997-2001 by Total Control Software
4# All Rights Reserved
5#------------------------------------------------------------------------
6#
7# Module Name: dbShelve.py
8#
9# Description: A reimplementation of the standard shelve.py that
10# forces the use of cPickle, and DB.
11#
12# Creation Date: 11/3/97 3:39:04PM
13#
14# License: This is free software. You may use this software for any
15# purpose including modification/redistribution, so long as
16# this header remains intact and that you do not claim any
17# rights of ownership or authorship of this software. This
18# software has been tested, but no warranty is expressed or
19# implied.
20#
21# 13-Dec-2000: Updated to be used with the new bsddb3 package.
22# Added DBShelfCursor class.
23#
24#------------------------------------------------------------------------
25
Barry Warsaw9a0d7792002-12-30 20:53:52 +000026"""Manage shelves of pickled objects using bsddb database files for the
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000027storage.
28"""
29
30#------------------------------------------------------------------------
31
Martin v. Löwis918f49e2007-08-08 22:08:30 +000032import pickle
Guido van Rossum8ce8a782007-11-01 19:42:39 +000033import sys
34
35#At version 2.3 cPickle switched to using protocol instead of bin and
36#DictMixin was added
37if sys.version_info[:3] >= (2, 3, 0):
38 HIGHEST_PROTOCOL = pickle.HIGHEST_PROTOCOL
39 def _dumps(object, protocol):
40 return pickle.dumps(object, protocol=protocol)
Raymond Hettinger3be449a2008-02-04 20:50:22 +000041 from UserDict import DictMixin
Guido van Rossum8ce8a782007-11-01 19:42:39 +000042else:
43 HIGHEST_PROTOCOL = None
44 def _dumps(object, protocol):
45 return pickle.dumps(object, bin=protocol)
Raymond Hettinger3be449a2008-02-04 20:50:22 +000046 class DictMixin: pass
Guido van Rossum8ce8a782007-11-01 19:42:39 +000047
Guido van Rossume2b70bc2006-08-18 22:13:04 +000048from . import db
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000049
Martin v. Löwiscccc58d2007-08-10 08:36:56 +000050_unspecified = object()
51
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000052#------------------------------------------------------------------------
53
54
Guido van Rossumcd16bf62007-06-13 18:07:49 +000055def open(filename, flags=db.DB_CREATE, mode=0o660, filetype=db.DB_HASH,
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000056 dbenv=None, dbname=None):
57 """
58 A simple factory function for compatibility with the standard
59 shleve.py module. It can be used like this, where key is a string
60 and data is a pickleable object:
61
Barry Warsaw9a0d7792002-12-30 20:53:52 +000062 from bsddb import dbshelve
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000063 db = dbshelve.open(filename)
64
65 db[key] = data
66
67 db.close()
68 """
69 if type(flags) == type(''):
70 sflag = flags
71 if sflag == 'r':
72 flags = db.DB_RDONLY
73 elif sflag == 'rw':
74 flags = 0
75 elif sflag == 'w':
76 flags = db.DB_CREATE
77 elif sflag == 'c':
78 flags = db.DB_CREATE
79 elif sflag == 'n':
80 flags = db.DB_TRUNCATE | db.DB_CREATE
81 else:
Collin Wintera65e94c2007-08-22 21:45:20 +000082 raise db.DBError("flags should be one of 'r', 'w', 'c' or 'n' or use the bsddb.db.DB_* flags")
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000083
84 d = DBShelf(dbenv)
85 d.open(filename, dbname, filetype, flags, mode)
86 return d
87
88#---------------------------------------------------------------------------
89
Gregory P. Smith5c5f1702007-10-12 19:13:19 +000090class DBShelveError(db.DBError): pass
91
92
Raymond Hettinger30a634e2003-02-05 04:12:41 +000093class DBShelf(DictMixin):
Barry Warsaw99142272003-02-08 03:18:58 +000094 """A shelf to hold pickled objects, built upon a bsddb DB object. It
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +000095 automatically pickles/unpickles data objects going to/from the DB.
96 """
97 def __init__(self, dbenv=None):
98 self.db = db.DB(dbenv)
Gregory P. Smith659e7f42007-10-13 23:23:58 +000099 self._closed = True
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000100 if HIGHEST_PROTOCOL:
101 self.protocol = HIGHEST_PROTOCOL
102 else:
103 self.protocol = 1
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000104
105
106 def __del__(self):
107 self.close()
108
109
110 def __getattr__(self, name):
Barry Warsaw99142272003-02-08 03:18:58 +0000111 """Many methods we can just pass through to the DB object.
112 (See below)
113 """
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000114 return getattr(self.db, name)
115
116
117 #-----------------------------------
118 # Dictionary access methods
119
120 def __len__(self):
121 return len(self.db)
122
123
124 def __getitem__(self, key):
125 data = self.db[key]
Martin v. Löwis918f49e2007-08-08 22:08:30 +0000126 return pickle.loads(data)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000127
128
129 def __setitem__(self, key, value):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000130 data = _dumps(value, self.protocol)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000131 self.db[key] = data
132
133
134 def __delitem__(self, key):
135 del self.db[key]
136
137
138 def keys(self, txn=None):
139 if txn != None:
140 return self.db.keys(txn)
141 else:
142 return self.db.keys()
143
144
Gregory P. Smith659e7f42007-10-13 23:23:58 +0000145 def open(self, *args, **kwargs):
146 self.db.open(*args, **kwargs)
147 self._closed = False
148
149
150 def close(self, *args, **kwargs):
151 self.db.close(*args, **kwargs)
152 self._closed = True
153
154
155 def __repr__(self):
156 if self._closed:
157 return '<DBShelf @ 0x%x - closed>' % (id(self))
158 else:
159 return repr(dict(self.iteritems()))
160
161
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000162 def items(self, txn=None):
163 if txn != None:
164 items = self.db.items(txn)
165 else:
166 items = self.db.items()
167 newitems = []
168
169 for k, v in items:
Martin v. Löwis918f49e2007-08-08 22:08:30 +0000170 newitems.append( (k, pickle.loads(v)) )
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000171 return newitems
172
173 def values(self, txn=None):
174 if txn != None:
175 values = self.db.values(txn)
176 else:
177 values = self.db.values()
178
Martin v. Löwis918f49e2007-08-08 22:08:30 +0000179 return map(pickle.loads, values)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000180
181 #-----------------------------------
182 # Other methods
183
Gregory P. Smith1281f762004-03-16 18:50:26 +0000184 def __append(self, value, txn=None):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000185 data = _dumps(value, self.protocol)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000186 return self.db.append(data, txn)
187
Gregory P. Smith1281f762004-03-16 18:50:26 +0000188 def append(self, value, txn=None):
Gregory P. Smith5c5f1702007-10-12 19:13:19 +0000189 if self.get_type() == db.DB_RECNO:
Gregory P. Smith659e7f42007-10-13 23:23:58 +0000190 return self.__append(value, txn=txn)
Gregory P. Smith5c5f1702007-10-12 19:13:19 +0000191 raise DBShelveError("append() only supported when dbshelve opened with filetype=dbshelve.db.DB_RECNO")
Gregory P. Smith1281f762004-03-16 18:50:26 +0000192
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000193
194 def associate(self, secondaryDB, callback, flags=0):
195 def _shelf_callback(priKey, priData, realCallback=callback):
Martin v. Löwis918f49e2007-08-08 22:08:30 +0000196 data = pickle.loads(priData)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000197 return realCallback(priKey, data)
198 return self.db.associate(secondaryDB, _shelf_callback, flags)
199
200
Martin v. Löwiscccc58d2007-08-10 08:36:56 +0000201 def get(self, key, default=_unspecified, txn=None, flags=0):
202 # If no default is given, we must not pass one to the
203 # extension module, so that an exception can be raised if
204 # set_get_returns_none is turned off.
205 if default is _unspecified:
206 data = self.db.get(key, txn=txn, flags=flags)
207 # if this returns, the default value would be None
208 default = None
209 else:
210 data = self.db.get(key, default, txn=txn, flags=flags)
211 if data is default:
212 return data
213 return pickle.loads(data)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000214
215 def get_both(self, key, value, txn=None, flags=0):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000216 data = _dumps(value, self.protocol)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000217 data = self.db.get(key, data, txn, flags)
Martin v. Löwis918f49e2007-08-08 22:08:30 +0000218 return pickle.loads(data)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000219
220
221 def cursor(self, txn=None, flags=0):
222 c = DBShelfCursor(self.db.cursor(txn, flags))
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000223 c.protocol = self.protocol
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000224 return c
225
226
227 def put(self, key, value, txn=None, flags=0):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000228 data = _dumps(value, self.protocol)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000229 return self.db.put(key, data, txn, flags)
230
231
232 def join(self, cursorList, flags=0):
233 raise NotImplementedError
234
235
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000236 def __contains__(self, key):
Guido van Rossum20435132006-08-21 00:21:47 +0000237 return self.db.has_key(key)
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000238
239
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000240 #----------------------------------------------
241 # Methods allowed to pass-through to self.db
242 #
243 # close, delete, fd, get_byteswapped, get_type, has_key,
244 # key_range, open, remove, rename, stat, sync,
245 # upgrade, verify, and all set_* methods.
246
247
248#---------------------------------------------------------------------------
249
250class DBShelfCursor:
251 """
252 """
253 def __init__(self, cursor):
254 self.dbc = cursor
255
256 def __del__(self):
257 self.close()
258
259
260 def __getattr__(self, name):
261 """Some methods we can just pass through to the cursor object. (See below)"""
262 return getattr(self.dbc, name)
263
264
265 #----------------------------------------------
266
267 def dup(self, flags=0):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000268 c = DBShelfCursor(self.dbc.dup(flags))
269 c.protocol = self.protocol
270 return c
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000271
272
273 def put(self, key, value, flags=0):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000274 data = _dumps(value, self.protocol)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000275 return self.dbc.put(key, data, flags)
276
277
278 def get(self, *args):
279 count = len(args) # a method overloading hack
280 method = getattr(self, 'get_%d' % count)
Neal Norwitzd9108552006-03-17 08:00:19 +0000281 method(*args)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000282
283 def get_1(self, flags):
284 rec = self.dbc.get(flags)
285 return self._extract(rec)
286
287 def get_2(self, key, flags):
288 rec = self.dbc.get(key, flags)
289 return self._extract(rec)
290
291 def get_3(self, key, value, flags):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000292 data = _dumps(value, self.protocol)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000293 rec = self.dbc.get(key, flags)
294 return self._extract(rec)
295
296
297 def current(self, flags=0): return self.get_1(flags|db.DB_CURRENT)
298 def first(self, flags=0): return self.get_1(flags|db.DB_FIRST)
299 def last(self, flags=0): return self.get_1(flags|db.DB_LAST)
300 def next(self, flags=0): return self.get_1(flags|db.DB_NEXT)
301 def prev(self, flags=0): return self.get_1(flags|db.DB_PREV)
302 def consume(self, flags=0): return self.get_1(flags|db.DB_CONSUME)
303 def next_dup(self, flags=0): return self.get_1(flags|db.DB_NEXT_DUP)
304 def next_nodup(self, flags=0): return self.get_1(flags|db.DB_NEXT_NODUP)
305 def prev_nodup(self, flags=0): return self.get_1(flags|db.DB_PREV_NODUP)
306
307
308 def get_both(self, key, value, flags=0):
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000309 data = _dumps(value, self.protocol)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000310 rec = self.dbc.get_both(key, flags)
311 return self._extract(rec)
312
313
314 def set(self, key, flags=0):
315 rec = self.dbc.set(key, flags)
316 return self._extract(rec)
317
318 def set_range(self, key, flags=0):
319 rec = self.dbc.set_range(key, flags)
320 return self._extract(rec)
321
322 def set_recno(self, recno, flags=0):
323 rec = self.dbc.set_recno(recno, flags)
324 return self._extract(rec)
325
326 set_both = get_both
327
328 def _extract(self, rec):
329 if rec is None:
330 return None
331 else:
332 key, data = rec
Martin v. Löwis918f49e2007-08-08 22:08:30 +0000333 return key, pickle.loads(data)
Martin v. Löwis6aa4a1f2002-11-19 08:09:52 +0000334
335 #----------------------------------------------
336 # Methods allowed to pass-through to self.dbc
337 #
338 # close, count, delete, get_recno, join_item
339
340
341#---------------------------------------------------------------------------