Code

- Remove implementations of Class.getnode from back_anydbm and rdbms_common,
[roundup.git] / roundup / backends / back_anydbm.py
index 8299415e12658d3cb1b88178b958bbfc710b443a..d6aebc43da3d022ad9d1a4cc8123de59a2cccdb8 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-#$Id: back_anydbm.py,v 1.120 2003-04-22 20:53:54 kedder Exp $
+#$Id: back_anydbm.py,v 1.132 2003-11-16 18:41:40 jlgijsbers Exp $
 '''
 This module defines a backend that saves the hyperdatabase in a database
 chosen by anydbm. It is guaranteed to always be available in python
@@ -23,7 +23,17 @@ versions >2.1.1 (the dumbdbm fallback in 2.1.1 and earlier has several
 serious bugs, and is not available)
 '''
 
-import whichdb, anydbm, os, marshal, re, weakref, string, copy
+try:
+    import anydbm, sys
+    # dumbdbm only works in python 2.1.2+
+    if sys.version_info < (2,1,2):
+        import dumbdbm
+        assert anydbm._defaultmod != dumbdbm
+        del dumbdbm
+except AssertionError:
+    print "WARNING: you should upgrade to python 2.1.3"
+
+import whichdb, os, marshal, re, weakref, string, copy
 from roundup import hyperdb, date, password, roundupdb, security
 from blobfiles import FileStorage
 from sessions import Sessions, OneTimeKeys
@@ -88,14 +98,9 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         if self.indexer.should_reindex():
             self.reindex()
 
-        # figure the "curuserid"
-        if self.journaltag is None:
-            self.curuserid = None
-        elif self.journaltag == 'admin':
-            # admin user may not exist, but always has ID 1
-            self.curuserid = '1'
-        else:
-            self.curuserid = self.user.lookup(self.journaltag)
+    def refresh_database(self):
+        "Rebuild the database"
+        self.reindex()
 
     def reindex(self):
         for klass in self.classes.values():
@@ -251,7 +256,7 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
             # add in the "calculated" properties (dupe so we don't affect
             # calling code's node assumptions)
             node = node.copy()
-            node['creator'] = self.curuserid
+            node['creator'] = self.getuid()
             node['creation'] = node['activity'] = date.Date()
 
         self.newnodes.setdefault(classname, {})[nodeid] = 1
@@ -283,17 +288,20 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
 
     def getnode(self, classname, nodeid, db=None, cache=1):
         ''' get a node from the database
+
+            Note the "cache" parameter is not used, and exists purely for
+            backward compatibility!
         '''
         if __debug__:
             print >>hyperdb.DEBUG, 'getnode', (self, classname, nodeid, db)
-        if cache:
-            # try the cache
-            cache_dict = self.cache.setdefault(classname, {})
-            if cache_dict.has_key(nodeid):
-                if __debug__:
-                    print >>hyperdb.TRACE, 'get %s %s cached'%(classname,
-                        nodeid)
-                return cache_dict[nodeid]
+
+        # try the cache
+        cache_dict = self.cache.setdefault(classname, {})
+        if cache_dict.has_key(nodeid):
+            if __debug__:
+                print >>hyperdb.TRACE, 'get %s %s cached'%(classname,
+                    nodeid)
+            return cache_dict[nodeid]
 
         if __debug__:
             print >>hyperdb.TRACE, 'get %s %s'%(classname, nodeid)
@@ -494,7 +502,7 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
                 cache_creator, cache_creation) = args
             if cache_classname == classname and cache_nodeid == nodeid:
                 if not cache_creator:
-                    cache_creator = self.curuserid
+                    cache_creator = self.getuid()
                 if not cache_creation:
                     cache_creation = date.Date()
                 res.append((cache_nodeid, cache_creation, cache_creator,
@@ -508,7 +516,11 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
             if str(error) == "need 'c' or 'n' flag to open new db":
                 raise IndexError, 'no such %s %s'%(classname, nodeid)
             elif error.args[0] != 2:
+                # this isn't a "not found" error, be alarmed!
                 raise
+            if res:
+                # we have unsaved journal entries, return them
+                return res
             raise IndexError, 'no such %s %s'%(classname, nodeid)
         try:
             journal = marshal.loads(db[nodeid])
@@ -570,15 +582,16 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         # keep a handle to all the database files opened
         self.databases = {}
 
-        # now, do all the transactions
-        reindex = {}
-        for method, args in self.transactions:
-            reindex[method(*args)] = 1
-
-        # now close all the database files
-        for db in self.databases.values():
-            db.close()
-        del self.databases
+        try:
+            # now, do all the transactions
+            reindex = {}
+            for method, args in self.transactions:
+                reindex[method(*args)] = 1
+        finally:
+            # make sure we close all the database files
+            for db in self.databases.values():
+                db.close()
+            del self.databases
 
         # reindex the nodes that request it
         for classname, nodeid in filter(None, reindex.keys()):
@@ -641,7 +654,7 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         if creator:
             journaltag = creator
         else:
-            journaltag = self.curuserid
+            journaltag = self.getuid()
         if creation:
             journaldate = creation.serialise()
         else:
@@ -927,7 +940,7 @@ class Class(hyperdb.Class):
             l.append(repr(value))
 
         # append retired flag
-        l.append(self.is_retired(nodeid))
+        l.append(repr(self.is_retired(nodeid)))
 
         return l
 
@@ -963,7 +976,7 @@ class Class(hyperdb.Class):
                     d[self.db.RETIRED_FLAG] = 1
                 continue
             elif value is None:
-                # don't set Nones
+                d[propname] = None
                 continue
 
             prop = properties[propname]
@@ -1008,10 +1021,7 @@ class Class(hyperdb.Class):
         IndexError is raised.  'propname' must be the name of a property
         of this class or a KeyError is raised.
 
-        'cache' indicates whether the transaction cache should be queried
-        for the node. If the node has been modified and you need to
-        determine what its values prior to modification are, you need to
-        set cache=0.
+        'cache' exists for backward compatibility, and is not used.
 
         Attempts to get the "creation" or "activity" properties should
         do the right thing.
@@ -1020,7 +1030,7 @@ class Class(hyperdb.Class):
             return nodeid
 
         # get the node's dict
-        d = self.db.getnode(self.classname, nodeid, cache=cache)
+        d = self.db.getnode(self.classname, nodeid)
 
         # check for one of the special props
         if propname == 'creation':
@@ -1064,7 +1074,7 @@ class Class(hyperdb.Class):
                         # user's been retired, return admin
                         return '1'
             else:
-                return self.db.curuserid
+                return self.db.getuid()
 
         # get the property (raises KeyErorr if invalid)
         prop = self.properties[propname]
@@ -1084,20 +1094,6 @@ class Class(hyperdb.Class):
 
         return d[propname]
 
-    # not in spec
-    def getnode(self, nodeid, cache=1):
-        ''' Return a convenience wrapper for the node.
-
-        'nodeid' must be the id of an existing node of this class or an
-        IndexError is raised.
-
-        'cache' indicates whether the transaction cache should be queried
-        for the node. If the node has been modified and you need to
-        determine what its values prior to modification are, you need to
-        set cache=0.
-        '''
-        return Node(self, nodeid, cache=cache)
-
     def set(self, nodeid, **propvalues):
         '''Modify a property on an existing node of this class.
         
@@ -1134,14 +1130,7 @@ class Class(hyperdb.Class):
         self.fireAuditors('set', nodeid, propvalues)
         # Take a copy of the node dict so that the subsequent set
         # operation doesn't modify the oldvalues structure.
-        try:
-            # try not using the cache initially
-            oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid,
-                cache=0))
-        except IndexError:
-            # this will be needed if somone does a create() and set()
-            # with no intervening commit()
-            oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid))
+        oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid))
 
         node = self.db.getnode(self.classname, nodeid)
         if node.has_key(self.db.RETIRED_FLAG):
@@ -1685,11 +1674,17 @@ class Class(hyperdb.Class):
                 u.sort()
                 l.append((MULTILINK, k, u))
             elif isinstance(propclass, String) and k != 'id':
-                # simple glob searching
-                v = re.sub(r'([\|\{\}\\\.\+\[\]\(\)])', r'\\\1', v)
-                v = v.replace('?', '.')
-                v = v.replace('*', '.*?')
-                l.append((STRING, k, re.compile(v, re.I)))
+                if type(v) is not type([]):
+                    v = [v]
+                m = []
+                for v in v:
+                    # simple glob searching
+                    v = re.sub(r'([\|\{\}\\\.\+\[\]\(\)])', r'\\\1', v)
+                    v = v.replace('?', '.')
+                    v = v.replace('*', '.*?')
+                    m.append(v)
+                m = re.compile('(%s)'%('|'.join(m)), re.I)
+                l.append((STRING, k, m))
             elif isinstance(propclass, Date):
                 try:
                     date_rng = Range(v, date.Date, offset=timezone)
@@ -1761,11 +1756,14 @@ class Class(hyperdb.Class):
                             continue
                         break
                     elif t == STRING:
+                        if node[k] is None:
+                            break
                         # RE search
-                        if node[k] is None or not v.search(node[k]):
+                        if not v.search(node[k]):
                             break
                     elif t == DATE or t == INTERVAL:
-                        if node[k] is None: break
+                        if node[k] is None:
+                            break
                         if v.to_value:
                             if not (v.from_value <= node[k] and v.to_value >= node[k]):
                                 break
@@ -1877,20 +1875,6 @@ class Class(hyperdb.Class):
                             r = cmp(bv, av)
                             if r != 0: return r
 
-                # Multilink properties are sorted according to how many
-                # links are present.
-                elif isinstance(propclass, Multilink):
-                    r = cmp(len(av), len(bv))
-                    if r == 0:
-                        # Compare contents of multilink property if lenghts is
-                        # equal
-                        r = cmp ('.'.join(av), '.'.join(bv))
-                    if r:
-                        if dir == '+':
-                            return r
-                        else:
-                            return -r
-
                 else:
                     # all other types just compare
                     if dir == '+':
@@ -2045,7 +2029,9 @@ class FileClass(Class, hyperdb.FileClass):
         return newid
 
     def get(self, nodeid, propname, default=_marker, cache=1):
-        ''' trap the content propname and get it from the file
+        ''' Trap the content propname and get it from the file
+
+        'cache' exists for backwards compatibility, and is not used.
         '''
         poss_msg = 'Possibly an access right configuration problem.'
         if propname == 'content':
@@ -2056,9 +2042,9 @@ class FileClass(Class, hyperdb.FileClass):
                 return 'ERROR reading file: %s%s\n%s\n%s'%(
                         self.classname, nodeid, poss_msg, strerror)
         if default is not _marker:
-            return Class.get(self, nodeid, propname, default, cache=cache)
+            return Class.get(self, nodeid, propname, default)
         else:
-            return Class.get(self, nodeid, propname, cache=cache)
+            return Class.get(self, nodeid, propname)
 
     def getprops(self, protected=1):
         ''' In addition to the actual properties on the node, these methods