changeset 1780:d2801a2b0a77

Initial implementation (half-baked) at new Tracker instance. Cleaned up caching API / comments in backends. Fixes to docs.
author Richard Jones <richard@users.sourceforge.net>
date Thu, 04 Sep 2003 00:47:01 +0000
parents fbe08359511a
children d51cb5225d4a
files CHANGES.txt doc/announcement.txt doc/customizing.txt roundup/backends/back_anydbm.py roundup/backends/back_metakit.py roundup/backends/rdbms_common.py roundup/hyperdb.py roundup/instance.py templates/classic/detectors/nosyreaction.py templates/classic/detectors/statusauditor.py test/test_db.py
diffstat 11 files changed, 128 insertions(+), 114 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Sun Aug 31 03:43:07 2003 +0000
+++ b/CHANGES.txt	Thu Sep 04 00:47:01 2003 +0000
@@ -1,7 +1,12 @@
 This file contains the changes to the Roundup system over time. The entries
 are given with the most recent entry first.
 
-2003-08-?? 0.6.1
+2003-09-?? 0.6.2
+Fixed:
+- cleaned up, clarified internal caching API in *dbm backends
+
+
+2003-08-31 0.6.1
 Fixed:
 - Add note about installing cgi-bin with a different interpreter
 - Importing wasn't setting None values explicitly when it should have been
--- a/doc/announcement.txt	Sun Aug 31 03:43:07 2003 +0000
+++ b/doc/announcement.txt	Thu Sep 04 00:47:01 2003 +0000
@@ -1,47 +1,38 @@
 =================================================
-SC-Track Roundup 0.6.0 - an issue tracking system
+SC-Track Roundup 0.6.1 - an issue tracking system
 =================================================
 
-I'm pleased to announce the latest feature-packed release of Roundup. See
-below for a list of some of the goodies included in this release.
+I'm pleased to announce this maintenance release of Roundup. This fix
+introduces Python2.3 compatibility. My thanks to Paul Dubois for
+contributing the csv module compatibility layer.
 
 If you're upgrading from an older version of Roundup you *must* follow
 the "Software Upgrade" guidelines given in the maintenance documentation. 
 
 Unfortunately, the Zope frontend for Roundup is currently broken. I hope to
-revive it in a future 0.6 bugfix release.
-
-The gadfly backend has now been removed, having served its purpose as a
-template for other RDBMS implementations. It is replaced by the sqlite and 
-mysql backends.
+revive it in a future 0.6 maintenance release.
 
 Roundup requires python 2.1.3 or later for correct operation.
 
-The 0.6 release has lots of new goodies including:
+This release fixes some bugs:
 
-- new instant-gratification Demo Mode ("python demo.py" :)
-- added mysql backend (see doc/mysql.txt for details)
-- web interface cleanups including nicer history display, nicer index
-  navigation and nicer popup list windows
-- searching of date ranges
-- better international support, including utf-8 email handling and ability
-  to display localized dates in web interface.
-- more documentation including revamped design document, unix manual pages
-  and some FAQ entries
-- significantly more powerful form handling allowing editing of multiple
-  items and creation of multiple items
-- tracker templates can contain subdirectories and static files (e.g.
-  images) and we may now distribute templates separately from Roundup.
-  Template HTML files now have a .html extension too.
-- user registration is now a two-step process, with confirmation from the
-  email address supplied in the registration form, and we also have a
-  password reset feature for forgotten password / login
-- Windows Service mode for roundup-server when daemonification is
-  attempted on Windows
-- lots of speed enhancements, making the web interface much more responsive
-- fixed issues with dumb email or web clients
-- email system handles more SMTP and POP features (TLS, APOP, ...)
-- lots more little tweaks and back-end work...
+- Add note about installing cgi-bin with a different interpreter
+- Importing wasn't setting None values explicitly when it should have been
+- Fixed import warning regarding 0xffff0000 literal, finally, really this
+  time. Checked on win2k. (sf bug 786711)
+- Fix CGI editCSV action to handle metakit's integer itemids
+- Apply fix for "remove" links from Klamer Schutte
+- Added permission check on "remove" link while I was there..
+- Applied CSV fix for python2.3 (sf bug 790363)
+- Fixed form padding in LHS menu (sf bug 790502)
+- Fixed upgrading docs for timezones (sf bug 790498)
+- Set the content type on page templates (can have XML templates now)
+- Various cosmetic fixes (thanks James Kew for being persistent :)
+- Applied patch 739314 (sorry John!)
+
+To give Roundup a try, just download (see below), unpack and run::
+
+    python demo.py
 
 Source and documentation is available at the website:
      http://roundup.sourceforge.net/
--- a/doc/customizing.txt	Sun Aug 31 03:43:07 2003 +0000
+++ b/doc/customizing.txt	Thu Sep 04 00:47:01 2003 +0000
@@ -2,7 +2,7 @@
 Customising Roundup
 ===================
 
-:Version: $Revision: 1.95 $
+:Version: $Revision: 1.96 $
 
 .. This document borrows from the ZopeBook section on ZPT. The original is at:
    http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -1483,7 +1483,7 @@
 There are several methods available on these wrapper objects:
 
 =========== ================================================================
-Method    Description
+Method      Description
 =========== ================================================================
 plain       render a "plain" representation of the property. This method
             may take two arguments:
--- a/roundup/backends/back_anydbm.py	Sun Aug 31 03:43:07 2003 +0000
+++ b/roundup/backends/back_anydbm.py	Thu Sep 04 00:47:01 2003 +0000
@@ -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.123 2003-08-26 00:06:55 richard Exp $
+#$Id: back_anydbm.py,v 1.124 2003-09-04 00:47:01 richard 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
@@ -283,17 +283,20 @@
 
     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)
@@ -1008,10 +1011,7 @@
         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 +1020,7 @@
             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':
@@ -1091,12 +1091,9 @@
         '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.
+        'cache' exists for backwards compatibility, and is not used.
         '''
-        return Node(self, nodeid, cache=cache)
+        return Node(self, nodeid)
 
     def set(self, nodeid, **propvalues):
         '''Modify a property on an existing node of this class.
@@ -1134,14 +1131,7 @@
         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):
@@ -2040,7 +2030,9 @@
         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':
@@ -2051,9 +2043,9 @@
                 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
--- a/roundup/backends/back_metakit.py	Sun Aug 31 03:43:07 2003 +0000
+++ b/roundup/backends/back_metakit.py	Thu Sep 04 00:47:01 2003 +0000
@@ -1,4 +1,4 @@
-# $Id: back_metakit.py,v 1.48 2003-08-26 00:06:56 richard Exp $
+# $Id: back_metakit.py,v 1.49 2003-09-04 00:47:01 richard Exp $
 '''
    Metakit backend for Roundup, originally by Gordon McMillan.
 
@@ -373,8 +373,9 @@
         return str(newid)
     
     def get(self, nodeid, propname, default=_marker, cache=1):
-        # default and cache aren't in the spec
-        # cache=0 means "original value"
+        '''
+            'cache' exists for backwards compatibility, and is not used.
+        '''
 
         view = self.getview()        
         id = int(nodeid)
@@ -1407,7 +1408,7 @@
         Class.__init__(self, db, classname, **properties)
 
     def get(self, nodeid, propname, default=_marker, cache=1):
-        x = Class.get(self, nodeid, propname, default, cache)
+        x = Class.get(self, nodeid, propname, default)
         poss_msg = 'Possibly an access right configuration problem.'
         if propname == 'content':
             if x.startswith('file:'):
--- a/roundup/backends/rdbms_common.py	Sun Aug 31 03:43:07 2003 +0000
+++ b/roundup/backends/rdbms_common.py	Thu Sep 04 00:47:01 2003 +0000
@@ -1,4 +1,4 @@
-# $Id: rdbms_common.py,v 1.59 2003-08-26 00:06:56 richard Exp $
+# $Id: rdbms_common.py,v 1.60 2003-09-04 00:47:01 richard Exp $
 ''' Relational database (SQL) backend common code.
 
 Basics:
@@ -1183,10 +1183,7 @@
         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 backwards compatibility, and is not used.
         '''
         if propname == 'id':
             return nodeid
@@ -1234,12 +1231,9 @@
         '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.
+        'cache' exists for backwards compatibility, and is not used.
         '''
-        return Node(self, nodeid, cache=cache)
+        return Node(self, nodeid)
 
     def set(self, nodeid, **propvalues):
         '''Modify a property on an existing node of this class.
@@ -2094,7 +2088,9 @@
 
     _marker = []
     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 a access right configuration problem.'
         if propname == 'content':
@@ -2105,9 +2101,9 @@
                 return 'ERROR reading file: %s%s\n%s\n%s'%(
                         self.classname, nodeid, poss_msg, strerror)
         if default is not self._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
--- a/roundup/hyperdb.py	Sun Aug 31 03:43:07 2003 +0000
+++ b/roundup/hyperdb.py	Thu Sep 04 00:47:01 2003 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: hyperdb.py,v 1.87 2003-03-17 22:03:03 kedder Exp $
+# $Id: hyperdb.py,v 1.88 2003-09-04 00:47:01 richard Exp $
 
 """
 Hyperdatabase implementation, especially field types.
@@ -245,6 +245,8 @@
 
     def getnode(self, classname, nodeid, db=None, cache=1):
         '''Get a node from the database.
+
+        'cache' exists for backwards compatibility, and is not used.
         '''
         raise NotImplementedError
 
@@ -365,10 +367,7 @@
         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 backwards compatibility, and is not used.
         """
         raise NotImplementedError
 
@@ -378,12 +377,9 @@
         '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.
+        'cache' exists for backwards compatibility, and is not used.
         '''
-        return Node(self, nodeid, cache=cache)
+        return Node(self, nodeid)
 
     def set(self, nodeid, **propvalues):
         """Modify a property on an existing node of this class.
@@ -580,18 +576,17 @@
     def __init__(self, cl, nodeid, cache=1):
         self.__dict__['cl'] = cl
         self.__dict__['nodeid'] = nodeid
-        self.__dict__['cache'] = cache
     def keys(self, protected=1):
         return self.cl.getprops(protected=protected).keys()
     def values(self, protected=1):
         l = []
         for name in self.cl.getprops(protected=protected).keys():
-            l.append(self.cl.get(self.nodeid, name, cache=self.cache))
+            l.append(self.cl.get(self.nodeid, name))
         return l
     def items(self, protected=1):
         l = []
         for name in self.cl.getprops(protected=protected).keys():
-            l.append((name, self.cl.get(self.nodeid, name, cache=self.cache)))
+            l.append((name, self.cl.get(self.nodeid, name)))
         return l
     def has_key(self, name):
         return self.cl.getprops().has_key(name)
@@ -604,7 +599,7 @@
         if self.__dict__.has_key(name):
             return self.__dict__[name]
         try:
-            return self.cl.get(self.nodeid, name, cache=self.cache)
+            return self.cl.get(self.nodeid, name)
         except KeyError, value:
             # we trap this but re-raise it as AttributeError - all other
             # exceptions should pass through untrapped
@@ -612,7 +607,7 @@
         # nope, no such attribute
         raise AttributeError, str(value)
     def __getitem__(self, name):
-        return self.cl.get(self.nodeid, name, cache=self.cache)
+        return self.cl.get(self.nodeid, name)
     def __setattr__(self, name, value):
         try:
             return self.cl.set(self.nodeid, **{name: value})
--- a/roundup/instance.py	Sun Aug 31 03:43:07 2003 +0000
+++ b/roundup/instance.py	Thu Sep 04 00:47:01 2003 +0000
@@ -15,21 +15,46 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: instance.py,v 1.9 2002-09-20 01:20:31 richard Exp $
+# $Id: instance.py,v 1.10 2003-09-04 00:47:01 richard Exp $
 
 __doc__ = '''
 Tracker handling (open tracker).
 
-Currently this module provides one function: open. This function opens
-a tracker. Note that trackers used to be called instances.
+Backwards compatibility for the old-style "imported" trackers.
 '''
 
-import imp, os
+import os
+
+class Vars:
+    ''' I'm just a container '''
+
+class Tracker:
+    def __init__(self, tracker_home):
+        self.tracker_home = tracker_home
+        self.select_db = self._load_python('select_db.py')
+        self.config = self._load_config('config.py')
+        raise NotImplemented, 'this is *so* not finished'
+        self.init =  XXX
+        self.Client = XXX
+        self.MailGW = XXX
+
+    def open(self):
+        return self._load_config('schema.py').db
+        self._load_config('security.py', db=db)
+
+
+    def __load_python(self, file):
+        file = os.path.join(tracker_home, file)
+        vars = Vars()
+        execfile(file, vars.__dict__)
+        return vars
+
 
 class TrackerError(Exception):
     pass
 
-class Opener:
+
+class OldStyleTrackers:
     def __init__(self):
         self.number = 0
         self.trackers = {}
@@ -39,6 +64,7 @@
 
             Raise ValueError if the tracker home doesn't exist.
         '''
+        import imp
         # sanity check existence of tracker home
         if not os.path.exists(tracker_home):
             raise ValueError, 'no such directory: "%s"'%tracker_home
@@ -67,11 +93,12 @@
 
         return tracker
 
-opener = Opener()
-open = opener.open
+OldStyleTrackers = OldStyleTrackers()
+def open(tracker_home):
+    if os.path.exists(os.path.join(tracker_home, 'dbinit.py')):
+        # user should upgrade...
+        return OldStyleTrackers.open(tracker_home)
 
-del Opener
-del opener
-
+    return Tracker(tracker_home)
 
 # vim: set filetype=python ts=4 sw=4 et si
--- a/templates/classic/detectors/nosyreaction.py	Sun Aug 31 03:43:07 2003 +0000
+++ b/templates/classic/detectors/nosyreaction.py	Thu Sep 04 00:47:01 2003 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-#$Id: nosyreaction.py,v 1.1 2003-04-17 03:26:38 richard Exp $
+#$Id: nosyreaction.py,v 1.2 2003-09-04 00:47:01 richard Exp $
 
 from roundup import roundupdb, hyperdb
 
@@ -107,8 +107,8 @@
         else:
             ok = ('yes',)
             # figure which of the messages now on the issue weren't
-            # there before - make sure we don't get a cached version!
-            oldmessages = cl.get(nodeid, 'messages', cache=0)
+            # there before
+            oldmessages = cl.get(nodeid, 'messages')
             messages = []
             for msgid in newvalues['messages']:
                 if msgid not in oldmessages:
--- a/templates/classic/detectors/statusauditor.py	Sun Aug 31 03:43:07 2003 +0000
+++ b/templates/classic/detectors/statusauditor.py	Thu Sep 04 00:47:01 2003 +0000
@@ -18,7 +18,7 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 #
-#$Id: statusauditor.py,v 1.2 2003-06-25 09:49:34 neaj Exp $
+#$Id: statusauditor.py,v 1.3 2003-09-04 00:47:01 richard Exp $
 
 def chatty(db, cl, nodeid, newvalues):
     ''' If the issue is currently 'unread', 'resolved' or 'done-cbb', then set
@@ -27,7 +27,7 @@
     # don't fire if there's no new message (ie. chat)
     if not newvalues.has_key('messages'):
         return
-    if newvalues['messages'] == cl.get(nodeid, 'messages', cache=0):
+    if newvalues['messages'] == cl.get(nodeid, 'messages'):
         return
 
     # get the chatting state ID
--- a/test/test_db.py	Sun Aug 31 03:43:07 2003 +0000
+++ b/test/test_db.py	Thu Sep 04 00:47:01 2003 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-# $Id: test_db.py,v 1.90 2003-08-12 02:22:22 richard Exp $ 
+# $Id: test_db.py,v 1.91 2003-09-04 00:47:01 richard Exp $ 
 
 import unittest, os, shutil, time
 
@@ -296,7 +296,14 @@
         self.assertNotEqual(a, self.db.status.list())
         # try to restore retired node
         self.db.status.restore('1')
-        self.assertEqual(a, self.db.status.list())
+ 
+    def testCacheCreateSet(self):
+        self.db.issue.create(title="spam", status='1')
+        a = self.db.issue.get('1', 'title')
+        self.assertEqual(a, 'spam')
+        self.db.issue.set('1', title='ham')
+        b = self.db.issue.get('1', 'title')
+        self.assertEqual(b, 'ham')
 
     def testSerialisation(self):
         nid = self.db.issue.create(title="spam", status='1',

Roundup Issue Tracker: http://roundup-tracker.org/