Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | #!/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 | ||
26 | """Manage shelves of pickled objects using bsddb database files for the | |
27 | storage. | |
28 | """ | |
29 | ||
30 | #------------------------------------------------------------------------ | |
31 | ||
32 | import cPickle | |
33 | try: | |
34 | from UserDict import DictMixin | |
35 | except ImportError: | |
36 | # DictMixin is new in Python 2.3 | |
37 | class DictMixin: pass | |
38 | import db | |
39 | ||
40 | #------------------------------------------------------------------------ | |
41 | ||
42 | ||
43 | def open(filename, flags=db.DB_CREATE, mode=0660, filetype=db.DB_HASH, | |
44 | dbenv=None, dbname=None): | |
45 | """ | |
46 | A simple factory function for compatibility with the standard | |
47 | shleve.py module. It can be used like this, where key is a string | |
48 | and data is a pickleable object: | |
49 | ||
50 | from bsddb import dbshelve | |
51 | db = dbshelve.open(filename) | |
52 | ||
53 | db[key] = data | |
54 | ||
55 | db.close() | |
56 | """ | |
57 | if type(flags) == type(''): | |
58 | sflag = flags | |
59 | if sflag == 'r': | |
60 | flags = db.DB_RDONLY | |
61 | elif sflag == 'rw': | |
62 | flags = 0 | |
63 | elif sflag == 'w': | |
64 | flags = db.DB_CREATE | |
65 | elif sflag == 'c': | |
66 | flags = db.DB_CREATE | |
67 | elif sflag == 'n': | |
68 | flags = db.DB_TRUNCATE | db.DB_CREATE | |
69 | else: | |
70 | raise db.DBError, "flags should be one of 'r', 'w', 'c' or 'n' or use the bsddb.db.DB_* flags" | |
71 | ||
72 | d = DBShelf(dbenv) | |
73 | d.open(filename, dbname, filetype, flags, mode) | |
74 | return d | |
75 | ||
76 | #--------------------------------------------------------------------------- | |
77 | ||
78 | class DBShelf(DictMixin): | |
79 | """A shelf to hold pickled objects, built upon a bsddb DB object. It | |
80 | automatically pickles/unpickles data objects going to/from the DB. | |
81 | """ | |
82 | def __init__(self, dbenv=None): | |
83 | self.db = db.DB(dbenv) | |
84 | self.binary = 1 | |
85 | ||
86 | ||
87 | def __del__(self): | |
88 | self.close() | |
89 | ||
90 | ||
91 | def __getattr__(self, name): | |
92 | """Many methods we can just pass through to the DB object. | |
93 | (See below) | |
94 | """ | |
95 | return getattr(self.db, name) | |
96 | ||
97 | ||
98 | #----------------------------------- | |
99 | # Dictionary access methods | |
100 | ||
101 | def __len__(self): | |
102 | return len(self.db) | |
103 | ||
104 | ||
105 | def __getitem__(self, key): | |
106 | data = self.db[key] | |
107 | return cPickle.loads(data) | |
108 | ||
109 | ||
110 | def __setitem__(self, key, value): | |
111 | data = cPickle.dumps(value, self.binary) | |
112 | self.db[key] = data | |
113 | ||
114 | ||
115 | def __delitem__(self, key): | |
116 | del self.db[key] | |
117 | ||
118 | ||
119 | def keys(self, txn=None): | |
120 | if txn != None: | |
121 | return self.db.keys(txn) | |
122 | else: | |
123 | return self.db.keys() | |
124 | ||
125 | ||
126 | def items(self, txn=None): | |
127 | if txn != None: | |
128 | items = self.db.items(txn) | |
129 | else: | |
130 | items = self.db.items() | |
131 | newitems = [] | |
132 | ||
133 | for k, v in items: | |
134 | newitems.append( (k, cPickle.loads(v)) ) | |
135 | return newitems | |
136 | ||
137 | def values(self, txn=None): | |
138 | if txn != None: | |
139 | values = self.db.values(txn) | |
140 | else: | |
141 | values = self.db.values() | |
142 | ||
143 | return map(cPickle.loads, values) | |
144 | ||
145 | #----------------------------------- | |
146 | # Other methods | |
147 | ||
148 | def __append(self, value, txn=None): | |
149 | data = cPickle.dumps(value, self.binary) | |
150 | return self.db.append(data, txn) | |
151 | ||
152 | def append(self, value, txn=None): | |
153 | if self.get_type() != db.DB_RECNO: | |
154 | self.append = self.__append | |
155 | return self.append(value, txn=txn) | |
156 | raise db.DBError, "append() only supported when dbshelve opened with filetype=dbshelve.db.DB_RECNO" | |
157 | ||
158 | ||
159 | def associate(self, secondaryDB, callback, flags=0): | |
160 | def _shelf_callback(priKey, priData, realCallback=callback): | |
161 | data = cPickle.loads(priData) | |
162 | return realCallback(priKey, data) | |
163 | return self.db.associate(secondaryDB, _shelf_callback, flags) | |
164 | ||
165 | ||
166 | #def get(self, key, default=None, txn=None, flags=0): | |
167 | def get(self, *args, **kw): | |
168 | # We do it with *args and **kw so if the default value wasn't | |
169 | # given nothing is passed to the extension module. That way | |
170 | # an exception can be raised if set_get_returns_none is turned | |
171 | # off. | |
172 | data = apply(self.db.get, args, kw) | |
173 | try: | |
174 | return cPickle.loads(data) | |
175 | except (TypeError, cPickle.UnpicklingError): | |
176 | return data # we may be getting the default value, or None, | |
177 | # so it doesn't need unpickled. | |
178 | ||
179 | def get_both(self, key, value, txn=None, flags=0): | |
180 | data = cPickle.dumps(value, self.binary) | |
181 | data = self.db.get(key, data, txn, flags) | |
182 | return cPickle.loads(data) | |
183 | ||
184 | ||
185 | def cursor(self, txn=None, flags=0): | |
186 | c = DBShelfCursor(self.db.cursor(txn, flags)) | |
187 | c.binary = self.binary | |
188 | return c | |
189 | ||
190 | ||
191 | def put(self, key, value, txn=None, flags=0): | |
192 | data = cPickle.dumps(value, self.binary) | |
193 | return self.db.put(key, data, txn, flags) | |
194 | ||
195 | ||
196 | def join(self, cursorList, flags=0): | |
197 | raise NotImplementedError | |
198 | ||
199 | ||
200 | #---------------------------------------------- | |
201 | # Methods allowed to pass-through to self.db | |
202 | # | |
203 | # close, delete, fd, get_byteswapped, get_type, has_key, | |
204 | # key_range, open, remove, rename, stat, sync, | |
205 | # upgrade, verify, and all set_* methods. | |
206 | ||
207 | ||
208 | #--------------------------------------------------------------------------- | |
209 | ||
210 | class DBShelfCursor: | |
211 | """ | |
212 | """ | |
213 | def __init__(self, cursor): | |
214 | self.dbc = cursor | |
215 | ||
216 | def __del__(self): | |
217 | self.close() | |
218 | ||
219 | ||
220 | def __getattr__(self, name): | |
221 | """Some methods we can just pass through to the cursor object. (See below)""" | |
222 | return getattr(self.dbc, name) | |
223 | ||
224 | ||
225 | #---------------------------------------------- | |
226 | ||
227 | def dup(self, flags=0): | |
228 | return DBShelfCursor(self.dbc.dup(flags)) | |
229 | ||
230 | ||
231 | def put(self, key, value, flags=0): | |
232 | data = cPickle.dumps(value, self.binary) | |
233 | return self.dbc.put(key, data, flags) | |
234 | ||
235 | ||
236 | def get(self, *args): | |
237 | count = len(args) # a method overloading hack | |
238 | method = getattr(self, 'get_%d' % count) | |
239 | apply(method, args) | |
240 | ||
241 | def get_1(self, flags): | |
242 | rec = self.dbc.get(flags) | |
243 | return self._extract(rec) | |
244 | ||
245 | def get_2(self, key, flags): | |
246 | rec = self.dbc.get(key, flags) | |
247 | return self._extract(rec) | |
248 | ||
249 | def get_3(self, key, value, flags): | |
250 | data = cPickle.dumps(value, self.binary) | |
251 | rec = self.dbc.get(key, flags) | |
252 | return self._extract(rec) | |
253 | ||
254 | ||
255 | def current(self, flags=0): return self.get_1(flags|db.DB_CURRENT) | |
256 | def first(self, flags=0): return self.get_1(flags|db.DB_FIRST) | |
257 | def last(self, flags=0): return self.get_1(flags|db.DB_LAST) | |
258 | def next(self, flags=0): return self.get_1(flags|db.DB_NEXT) | |
259 | def prev(self, flags=0): return self.get_1(flags|db.DB_PREV) | |
260 | def consume(self, flags=0): return self.get_1(flags|db.DB_CONSUME) | |
261 | def next_dup(self, flags=0): return self.get_1(flags|db.DB_NEXT_DUP) | |
262 | def next_nodup(self, flags=0): return self.get_1(flags|db.DB_NEXT_NODUP) | |
263 | def prev_nodup(self, flags=0): return self.get_1(flags|db.DB_PREV_NODUP) | |
264 | ||
265 | ||
266 | def get_both(self, key, value, flags=0): | |
267 | data = cPickle.dumps(value, self.binary) | |
268 | rec = self.dbc.get_both(key, flags) | |
269 | return self._extract(rec) | |
270 | ||
271 | ||
272 | def set(self, key, flags=0): | |
273 | rec = self.dbc.set(key, flags) | |
274 | return self._extract(rec) | |
275 | ||
276 | def set_range(self, key, flags=0): | |
277 | rec = self.dbc.set_range(key, flags) | |
278 | return self._extract(rec) | |
279 | ||
280 | def set_recno(self, recno, flags=0): | |
281 | rec = self.dbc.set_recno(recno, flags) | |
282 | return self._extract(rec) | |
283 | ||
284 | set_both = get_both | |
285 | ||
286 | def _extract(self, rec): | |
287 | if rec is None: | |
288 | return None | |
289 | else: | |
290 | key, data = rec | |
291 | return key, cPickle.loads(data) | |
292 | ||
293 | #---------------------------------------------- | |
294 | # Methods allowed to pass-through to self.dbc | |
295 | # | |
296 | # close, count, delete, get_recno, join_item | |
297 | ||
298 | ||
299 | #--------------------------------------------------------------------------- |