1515# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717#
18- #$Id: back_anydbm.py,v 1.16 2001-12-12 03: 23:14 richard Exp $
18+ #$Id: back_anydbm.py,v 1.17 2001-12-14 23:42:57 richard Exp $
1919'''
2020This module defines a backend that saves the hyperdatabase in a database
2121chosen by anydbm. It is guaranteed to always be available in python
2626import whichdb , anydbm , os , marshal
2727from roundup import hyperdb , date , password
2828
29+ DEBUG = os .environ .get ('HYPERDBDEBUG' , '' )
30+
2931#
3032# Now the database
3133#
@@ -58,21 +60,32 @@ def __init__(self, storagelocator, journaltag=None):
5860 self .newnodes = {} # keep track of the new nodes by class
5961 self .transactions = []
6062
63+ def __repr__ (self ):
64+ return '<back_anydbm instance at %x>' % id (self )
65+
6166 #
6267 # Classes
6368 #
6469 def __getattr__ (self , classname ):
6570 """A convenient way of calling self.getclass(classname)."""
66- return self .classes [classname ]
71+ if self .classes .has_key (classname ):
72+ if DEBUG :
73+ print '__getattr__' , (self , classname )
74+ return self .classes [classname ]
75+ raise AttributeError , classname
6776
6877 def addclass (self , cl ):
78+ if DEBUG :
79+ print 'addclass' , (self , cl )
6980 cn = cl .classname
7081 if self .classes .has_key (cn ):
7182 raise ValueError , cn
7283 self .classes [cn ] = cl
7384
7485 def getclasses (self ):
7586 """Return a list of the names of all existing classes."""
87+ if DEBUG :
88+ print 'getclasses' , (self ,)
7689 l = self .classes .keys ()
7790 l .sort ()
7891 return l
@@ -82,6 +95,8 @@ def getclass(self, classname):
8295
8396 If 'classname' is not a valid class name, a KeyError is raised.
8497 """
98+ if DEBUG :
99+ print 'getclass' , (self , classname )
85100 return self .classes [classname ]
86101
87102 #
@@ -90,6 +105,8 @@ def getclass(self, classname):
90105 def clear (self ):
91106 '''Delete all database contents
92107 '''
108+ if DEBUG :
109+ print 'clear' , (self ,)
93110 for cn in self .classes .keys ():
94111 for type in 'nodes' , 'journals' :
95112 path = os .path .join (self .dir , 'journals.%s' % cn )
@@ -102,12 +119,16 @@ def getclassdb(self, classname, mode='r'):
102119 ''' grab a connection to the class db that will be used for
103120 multiple actions
104121 '''
122+ if DEBUG :
123+ print 'getclassdb' , (self , classname , mode )
105124 return self ._opendb ('nodes.%s' % classname , mode )
106125
107126 def _opendb (self , name , mode ):
108127 '''Low-level database opener that gets around anydbm/dbm
109128 eccentricities.
110129 '''
130+ if DEBUG :
131+ print '_opendb' , (self , name , mode )
111132 # determine which DB wrote the class file
112133 db_type = ''
113134 path = os .path .join (os .getcwd (), self .dir , name )
@@ -122,6 +143,8 @@ def _opendb(self, name, mode):
122143
123144 # new database? let anydbm pick the best dbm
124145 if not db_type :
146+ if DEBUG :
147+ print "_opendb anydbm.open(%r, 'n')" % path
125148 return anydbm .open (path , 'n' )
126149
127150 # open the database with the correct module
@@ -131,6 +154,8 @@ def _opendb(self, name, mode):
131154 raise hyperdb .DatabaseError , \
132155 "Couldn't open database - the required module '%s'" \
133156 "is not available" % db_type
157+ if DEBUG :
158+ print "_opendb %r.open(%r, %r)" % (db_type , path , mode )
134159 return dbm .open (path , mode )
135160
136161 #
@@ -139,13 +164,17 @@ def _opendb(self, name, mode):
139164 def addnode (self , classname , nodeid , node ):
140165 ''' add the specified node to its class's db
141166 '''
167+ if DEBUG :
168+ print 'addnode' , (self , classname , nodeid , node )
142169 self .newnodes .setdefault (classname , {})[nodeid ] = 1
143170 self .cache .setdefault (classname , {})[nodeid ] = node
144171 self .savenode (classname , nodeid , node )
145172
146173 def setnode (self , classname , nodeid , node ):
147174 ''' change the specified node
148175 '''
176+ if DEBUG :
177+ print 'setnode' , (self , classname , nodeid , node )
149178 self .dirtynodes .setdefault (classname , {})[nodeid ] = 1
150179 # can't set without having already loaded the node
151180 self .cache [classname ][nodeid ] = node
@@ -154,51 +183,65 @@ def setnode(self, classname, nodeid, node):
154183 def savenode (self , classname , nodeid , node ):
155184 ''' perform the saving of data specified by the set/addnode
156185 '''
186+ if DEBUG :
187+ print 'savenode' , (self , classname , nodeid , node )
157188 self .transactions .append ((self ._doSaveNode , (classname , nodeid , node )))
158189
159- def getnode (self , classname , nodeid , cldb = None ):
190+ def getnode (self , classname , nodeid , db = None ):
160191 ''' add the specified node to its class's db
161192 '''
193+ if DEBUG :
194+ print 'getnode' , (self , classname , nodeid , cldb )
162195 # try the cache
163196 cache = self .cache .setdefault (classname , {})
164197 if cache .has_key (nodeid ):
165198 return cache [nodeid ]
166199
167200 # get from the database and save in the cache
168- db = cldb or self .getclassdb (classname )
201+ if db is None :
202+ db = self .getclassdb (classname )
169203 if not db .has_key (nodeid ):
170204 raise IndexError , nodeid
171205 res = marshal .loads (db [nodeid ])
172206 cache [nodeid ] = res
173207 return res
174208
175- def hasnode (self , classname , nodeid , cldb = None ):
209+ def hasnode (self , classname , nodeid , db = None ):
176210 ''' add the specified node to its class's db
177211 '''
212+ if DEBUG :
213+ print 'hasnode' , (self , classname , nodeid , cldb )
178214 # try the cache
179215 cache = self .cache .setdefault (classname , {})
180216 if cache .has_key (nodeid ):
181217 return 1
182218
183219 # not in the cache - check the database
184- db = cldb or self .getclassdb (classname )
220+ if db is None :
221+ db = self .getclassdb (classname )
185222 res = db .has_key (nodeid )
186223 return res
187224
188- def countnodes (self , classname , cldb = None ):
225+ def countnodes (self , classname , db = None ):
226+ if DEBUG :
227+ print 'countnodes' , (self , classname , cldb )
189228 # include the new nodes not saved to the DB yet
190229 count = len (self .newnodes .get (classname , {}))
191230
192231 # and count those in the DB
193- db = cldb or self .getclassdb (classname )
232+ if db is None :
233+ db = self .getclassdb (classname )
194234 count = count + len (db .keys ())
195235 return count
196236
197- def getnodeids (self , classname , cldb = None ):
237+ def getnodeids (self , classname , db = None ):
238+ if DEBUG :
239+ print 'getnodeids' , (self , classname , db )
198240 # start off with the new nodes
199241 res = self .newnodes .get (classname , {}).keys ()
200242
201- db = cldb or self .getclassdb (classname )
243+ if db is None :
244+ db = self .getclassdb (classname )
202245 res = res + db .keys ()
203246 return res
204247
@@ -213,12 +256,16 @@ def addjournal(self, classname, nodeid, action, params):
213256 'link' or 'unlink' -- 'params' is (classname, nodeid, propname)
214257 'retire' -- 'params' is None
215258 '''
259+ if DEBUG :
260+ print 'addjournal' , (self , classname , nodeid , action , params )
216261 self .transactions .append ((self ._doSaveJournal , (classname , nodeid ,
217262 action , params )))
218263
219264 def getjournal (self , classname , nodeid ):
220265 ''' get the journal for id
221266 '''
267+ if DEBUG :
268+ print 'getjournal' , (self , classname , nodeid )
222269 # attempt to open the journal - in some rare cases, the journal may
223270 # not exist
224271 try :
@@ -242,6 +289,8 @@ def getjournal(self, classname, nodeid):
242289 def commit (self ):
243290 ''' Commit the current transactions.
244291 '''
292+ if DEBUG :
293+ print 'commit' , (self ,)
245294 # lock the DB
246295 for method , args in self .transactions :
247296 # TODO: optimise this, duh!
@@ -255,15 +304,19 @@ def commit(self):
255304 self .transactions = []
256305
257306 def _doSaveNode (self , classname , nodeid , node ):
307+ if DEBUG :
308+ print '_doSaveNode' , (self , classname , nodeid , node )
258309 db = self .getclassdb (classname , 'c' )
259310 # now save the marshalled data
260311 db [nodeid ] = marshal .dumps (node )
261312 db .close ()
262313
263314 def _doSaveJournal (self , classname , nodeid , action , params ):
315+ if DEBUG :
316+ print '_doSaveJournal' , (self , classname , nodeid , action , params )
264317 entry = (nodeid , date .Date ().get_tuple (), self .journaltag , action ,
265318 params )
266- db = anydbm . open ( os . path . join ( self .dir , 'journals.%s' % classname ) , 'c' )
319+ db = self ._opendb ( 'journals.%s' % classname , 'c' )
267320 if db .has_key (nodeid ):
268321 s = db [nodeid ]
269322 l = marshal .loads (db [nodeid ])
@@ -276,13 +329,21 @@ def _doSaveJournal(self, classname, nodeid, action, params):
276329 def rollback (self ):
277330 ''' Reverse all actions from the current transaction.
278331 '''
332+ if DEBUG :
333+ print 'rollback' , (self , )
279334 self .cache = {}
280335 self .dirtynodes = {}
281336 self .newnodes = {}
282337 self .transactions = []
283338
284339#
285340#$Log: not supported by cvs2svn $
341+ #Revision 1.16 2001/12/12 03:23:14 richard
342+ #Cor blimey this anydbm/whichdb stuff is yecchy. Turns out that whichdb
343+ #incorrectly identifies a dbm file as a dbhash file on my system. This has
344+ #been submitted to the python bug tracker as issue #491888:
345+ #https://sourceforge.net/tracker/index.php?func=detail&aid=491888&group_id=5470&atid=105470
346+ #
286347#Revision 1.15 2001/12/12 02:30:51 richard
287348#I fixed the problems with people whose anydbm was using the dbm module at the
288349#backend. It turns out the dbm module modifies the file name to append ".db"
0 commit comments