Revision: 8116
          http://svn.sourceforge.net/mailman/?rev=8116&view=rev
Author:   bwarsaw
Date:     2006-12-11 04:27:47 -0800 (Mon, 11 Dec 2006)

Log Message:
-----------
Rework the whole dbcontext and transaction framework.  SA already handles
nested transactions so we don't have to worry about them.  However, we do have
the weird situation where some transactions are tied to MailList
.Lock()/.Unlock()/.Save() and some are tied to non-mlist actions.  So now we
use an @txn decorator to put methods in a session transaction, but then we
also hook into the above MailList methods as possibly sub-transactions.  We
use a weakref subclass to manage the MailList interface, with a dictionary
mapping MailList fqdn_listnames against transactions.  The weakrefs come in by
giving us a callback when a MailList gets derefed such that we're guaranteed
to rollback any outstanding transaction.

Also, we have one global DBContext instance but rather than force the rest of
Mailman to deal with context objects, instead we expose API methods on that
object into the Mailman.database module, which the rest of the code will use.
Such methods must be prepended with 'api_' to get exposed this way.

bin/rmlist now works with the SA-backend.  I refactored the code here so that
other code (namely, the test suite) can more easily and consistently remove a
mailing list.  This isn't the best place for it ultimately, but it's good
enough for now.

New convenience functions Utils.split_listname(), .fqdn_listname().

Convert testall to use Mailman.initialize.initialize().  Not all tests work,
but I'm down to only 8 failures and 7 errors.  Also, do a better job of
recovering from failures in setUp().

MailList.__new__() now takes keyword arguments.

Modified Paths:
--------------
    branches/tmp-sqlalchemy-branch/Mailman/MailList.py
    branches/tmp-sqlalchemy-branch/Mailman/Utils.py
    branches/tmp-sqlalchemy-branch/Mailman/bin/rmlist.py
    branches/tmp-sqlalchemy-branch/Mailman/bin/testall.py
    branches/tmp-sqlalchemy-branch/Mailman/database/__init__.py
    branches/tmp-sqlalchemy-branch/Mailman/initialize.py
    branches/tmp-sqlalchemy-branch/Mailman/testing/base.py
    branches/tmp-sqlalchemy-branch/Mailman/testing/emailbase.py

Added Paths:
-----------
    branches/tmp-sqlalchemy-branch/Mailman/database/dbcontext.py
    branches/tmp-sqlalchemy-branch/Mailman/database/txnsupport.py

Modified: branches/tmp-sqlalchemy-branch/Mailman/MailList.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/MailList.py  2006-12-07 14:13:56 UTC 
(rev 8115)
+++ branches/tmp-sqlalchemy-branch/Mailman/MailList.py  2006-12-11 12:27:47 UTC 
(rev 8116)
@@ -46,9 +46,9 @@
 from Mailman import LockFile
 from Mailman import Utils
 from Mailman import Version
+from Mailman import database
 from Mailman.UserDesc import UserDesc
 from Mailman.configuration import config
-from Mailman.database import dbcontext
 
 # Base classes
 from Mailman import Pending
@@ -88,17 +88,24 @@
 class MailList(object, HTMLFormatter, Deliverer, ListAdmin,
                Archiver, Digester, SecurityManager, Bouncer, GatewayManager,
                Autoresponder, TopicMgr, Pending.Pending):
-    def __new__(cls, *args):
-        if not args:
-            # We're probably being created from the database query, so just
-            # let the super class do its thing.
-            return super(MailList, cls).__new__(cls, *args)
-        # Some one is opening the mailing list, so do a query
-        if '@' in args[0]:
-            fqdn_listname = args[0]
+    def __new__(cls, *args, **kws):
+        # Search positional and keyword arguments to find the name of the
+        # existing list that is being opened, with the latter taking
+        # precedence.  If no name can be found, then make sure there are no
+        # arguments, otherwise it's an error.
+        if 'name' in kws:
+            listname = kws.pop('name')
+        elif not args:
+            if not kws:
+                # We're probably being created from the ORM layer, so just let
+                # the super class do its thing.
+                return super(MailList, cls).__new__(cls, *args, **kws)
+            raise ValueError("'name' argument required'")
         else:
-            fqdn_listname = '[EMAIL PROTECTED]' % (args[0], 
config.DEFAULT_EMAIL_HOST)
-        mlist = dbcontext.get_list(fqdn_listname)
+            listname = args[0]
+        fqdn_listname = Utils.fqdn_listname(listname)
+        listname, hostname = Utils.split_listname(fqdn_listname)
+        mlist = database.find_list(listname, hostname)
         if not mlist:
             raise Errors.MMUnknownListError(fqdn_listname)
         return mlist
@@ -173,7 +180,7 @@
     # Lock management
     #
     def Lock(self, timeout=0):
-        dbcontext.begin()
+        database.lock(self)
         self.__lock.lock(timeout)
         self._memberadaptor.lock()
         # Must reload our database for consistency.  Watch out for lists that
@@ -185,7 +192,7 @@
             raise
 
     def Unlock(self):
-        dbcontext.rollback()
+        database.unlock(self)
         self.__lock.unlock(unconditionally=True)
         self._memberadaptor.unlock()
 
@@ -544,8 +551,6 @@
         Utils.makedirs(self._full_path)
         # Don't use Lock() since that tries to load the non-existant config.pck
         self.__lock.lock()
-        dbcontext.begin()
-        dbcontext.session.save(self)
         # We need to set this attribute before calling InitVars() because
         # Archiver.InitVars() depends on this to calculate and create the
         # archive directory.  Without this, Archiver will create a bogus
@@ -559,6 +564,7 @@
             self.available_languages = langs
         url_host = config.domains[email_host]
         self.web_page_url = config.DEFAULT_URL_PATTERN % url_host
+        database.add_list(self)
 
 
 
@@ -569,7 +575,7 @@
         # defensive?
         self.__lock.refresh()
         # Commit the database transaction
-        dbcontext.commit()
+        database.save(self)
         # The member adaptor may have its own save operation
         self._memberadaptor.save()
         self.SaveRequestsDb()

Modified: branches/tmp-sqlalchemy-branch/Mailman/Utils.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/Utils.py     2006-12-07 14:13:56 UTC 
(rev 8115)
+++ branches/tmp-sqlalchemy-branch/Mailman/Utils.py     2006-12-11 12:27:47 UTC 
(rev 8116)
@@ -40,9 +40,9 @@
 from string import ascii_letters, digits, whitespace
 
 from Mailman import Errors
+from Mailman import database
 from Mailman.SafeDict import SafeDict
 from Mailman.configuration import config
-from Mailman.database import dbcontext
 
 # REMOVEME when Python 2.4 is minimum requirement
 try:
@@ -51,12 +51,13 @@
     from sets import Set as set
 
 
-EMPTYSTRING = ''
-UEMPTYSTRING = u''
+AT = '@'
 CR = '\r'
-NL = '\n'
 DOT = '.'
+EMPTYSTRING = ''
 IDENTCHARS = ascii_letters + digits + '_'
+NL = '\n'
+UEMPTYSTRING = u''
 
 # Search for $(identifier)s strings, except that the trailing s is optional,
 # since that's a common mistake
@@ -70,7 +71,8 @@
 
 def list_exists(fqdn_listname):
     """Return true iff list `fqdn_listname' exists."""
-    return bool(dbcontext.get_list(fqdn_listname))
+    listname, hostname = split_listname(fqdn_listname)
+    return bool(database.find_list(listname, hostname))
 
 
 def list_names():
@@ -82,6 +84,16 @@
     return got
 
 
+def split_listname(listname):
+    if AT in listname:
+        return listname.split(AT, 1)
+    return listname, config.DEFAULT_EMAIL_HOST
+
+
+def fqdn_listname(listname):
+    return AT.join(split_listname(listname))
+
+
 
 # a much more naive implementation than say, Emacs's fill-paragraph!
 def wrap(text, column=70, honor_leading_ws=True):

Modified: branches/tmp-sqlalchemy-branch/Mailman/bin/rmlist.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/bin/rmlist.py        2006-12-07 
14:13:56 UTC (rev 8115)
+++ branches/tmp-sqlalchemy-branch/Mailman/bin/rmlist.py        2006-12-11 
12:27:47 UTC (rev 8116)
@@ -20,30 +20,74 @@
 import shutil
 import optparse
 
-from Mailman import MailList
+from Mailman import Errors
 from Mailman import Utils
 from Mailman import Version
+from Mailman import database
+from Mailman.MailList import MailList
 from Mailman.configuration import config
 from Mailman.i18n import _
+from Mailman.initialize import initialize
 
 __i18n_templates__ = True
 
 
 
-def remove_it(listname, filename, msg):
+def remove_it(listname, filename, msg, quiet=False):
     if os.path.islink(filename):
-        print _('Removing $msg')
+        if not quiet:
+            print _('Removing $msg')
         os.unlink(filename)
     elif os.path.isdir(filename):
-        print _('Removing $msg')
+        if not quiet:
+            print _('Removing $msg')
         shutil.rmtree(filename)
     elif os.path.isfile(filename):
         os.unlink(filename)
     else:
-        print _('$listname $msg not found as $filename')
+        if not quiet:
+            print _('$listname $msg not found as $filename')
 
 
 
+def delete_list(listname, mlist=None, archives=True, quiet=False):
+    removeables = []
+    if mlist:
+        # Remove the list from the database
+        database.remove_list(mlist)
+        # Do the MTA-specific list deletion tasks
+        if config.MTA:
+            modname = 'Mailman.MTA.' + config.MTA
+            __import__(modname)
+            sys.modules[modname].remove(mlist)
+        # Remove the list directory
+        removeables.append((os.path.join('lists', listname), _('list info')))
+
+    # Remove any stale locks associated with the list
+    for filename in os.listdir(config.LOCK_DIR):
+        fn_listname = filename.split('.')[0]
+        if fn_listname == listname:
+            removeables.append((os.path.join(config.LOCK_DIR, filename),
+                                _('stale lock file')))
+
+    if archives:
+        removeables.extend([
+            (os.path.join('archives', 'private', listname),
+             _('private archives')),
+            (os.path.join('archives', 'private', listname + '.mbox'),
+             _('private archives')),
+            (os.path.join('archives', 'public', listname),
+             _('public archives')),
+            (os.path.join('archives', 'public', listname + '.mbox'),
+             _('public archives')),
+            ])
+
+    for dirtmpl, msg in removeables:
+        path = os.path.join(config.VAR_PREFIX, dirtmpl)
+        remove_it(listname, path, msg, quiet)
+
+
+
 def parseargs():
     parser = optparse.OptionParser(version=Version.MAILMAN_VERSION,
                                    usage=_("""\
@@ -75,13 +119,12 @@
 
 def main():
     parser, opts, args = parseargs()
-    config.load(opts.config)
+    initialize(opts.config)
 
-    listname = args[0].lower().strip()
-    if '@' not in listname:
-        listname = '[EMAIL PROTECTED]' % (listname, config.DEFAULT_EMAIL_HOST)
-
-    if not Utils.list_exists(listname):
+    listname = Utils.fqdn_listname(args[0])
+    try:
+        mlist = MailList(listname, lock=False)
+    except Errors.MMUnknownListError:
         if not opts.archives:
             print >> sys.stderr, _(
                 'No such list (or list already deleted): $listname')
@@ -89,45 +132,14 @@
         else:
             print _(
                 'No such list: ${listname}.  Removing its residual archives.')
+            mlist = None
 
     if not opts.archives:
         print _('Not removing archives.  Reinvoke with -a to remove them.')
 
-    removeables = []
-    if Utils.list_exists(listname):
-        mlist = MailList.MailList(listname, lock=False)
-        # Do the MTA-specific list deletion tasks
-        if config.MTA:
-            modname = 'Mailman.MTA.' + config.MTA
-            __import__(modname)
-            sys.modules[modname].remove(mlist)
+    delete_list(listname, mlist, opts.archives)
 
-        removeables.append((os.path.join('lists', listname), _('list info')))
 
-    # Remove any stale locks associated with the list
-    for filename in os.listdir(config.LOCK_DIR):
-        fn_listname = filename.split('.')[0]
-        if fn_listname == listname:
-            removeables.append((os.path.join(config.LOCK_DIR, filename),
-                                _('stale lock file')))
-
-    if opts.archives:
-        removeables.extend([
-            (os.path.join('archives', 'private', listname),
-             _('private archives')),
-            (os.path.join('archives', 'private', listname + '.mbox'),
-             _('private archives')),
-            (os.path.join('archives', 'public', listname),
-             _('public archives')),
-            (os.path.join('archives', 'public', listname + '.mbox'),
-             _('public archives')),
-            ])
-
-    for dirtmpl, msg in removeables:
-        path = os.path.join(config.VAR_PREFIX, dirtmpl)
-        remove_it(listname, path, msg)
-
-
 
 if __name__ == '__main__':
     main()

Modified: branches/tmp-sqlalchemy-branch/Mailman/bin/testall.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/bin/testall.py       2006-12-07 
14:13:56 UTC (rev 8115)
+++ branches/tmp-sqlalchemy-branch/Mailman/bin/testall.py       2006-12-11 
12:27:47 UTC (rev 8116)
@@ -25,8 +25,8 @@
 
 from Mailman import Version
 from Mailman import loginit
-from Mailman.configuration import config
 from Mailman.i18n import _
+from Mailman.initialize import initialize
 
 __i18n_templates__ = True
 
@@ -138,7 +138,7 @@
     global basedir
 
     parser, opts, args = parseargs()
-    config.load(opts.config)
+    initialize(opts.config)
     if not args:
         args = ['.']
     loginit.initialize(propagate=opts.stderr)

Modified: branches/tmp-sqlalchemy-branch/Mailman/database/__init__.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/database/__init__.py 2006-12-07 
14:13:56 UTC (rev 8115)
+++ branches/tmp-sqlalchemy-branch/Mailman/database/__init__.py 2006-12-11 
12:27:47 UTC (rev 8116)
@@ -15,77 +15,20 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 # USA.
 
-from sqlalchemy import *
-from string import Template
+# This module exposes the higher level interface methods that the rest of
+# Mailman should use.  It essentially hides the dbcontext and the SQLAlchemy
+# session from all other code.  The preferred way to use these methods is:
+#
+# from Mailman import database
+# database.add_list(foo)
 
-from Mailman import Version
-from Mailman.configuration import config
-from Mailman.database import address 
-from Mailman.database import listdata
-from Mailman.database import version
 
+def initialize():
+    from Mailman import database
+    from Mailman.database.dbcontext import dbcontext
 
-
-class DBContext(object):
-    def __init__(self):
-        self.tables = {}
-        self.metadata = None
-        self.session = None
-        self._txn = None
-
-    def connect(self):
-        # Calculate the engine url
-        url = Template(config.SQLALCHEMY_ENGINE_URL).safe_substitute(
-            config.paths)
-        self.metadata = BoundMetaData(url)
-        self.metadata.engine.echo = config.SQLALCHEMY_ECHO
-        # Create all the table objects, and then let SA conditionally create
-        # them if they don't yet exist.
-        version_table = None
-        for module in (address, listdata, version):
-            table = module.make_table(self.metadata)
-            self.tables[table.name] = table
-            if module is version:
-                version_table = table
-        self.metadata.create_all()
-        # Validate schema version, updating if necessary (XXX)
-        from Mailman.interact import interact
-        r = version_table.select(version_table.c.component=='schema').execute()
-        row = r.fetchone()
-        if row is None:
-            # Database has not yet been initialized
-            version_table.insert().execute(
-                component='schema',
-                version=Version.DATABASE_SCHEMA_VERSION)
-        elif row.version <> Version.DATABASE_SCHEMA_VERSION:
-            # XXX Update schema
-            raise SchemaVersionMismatchError(row.version)
-        self.session = create_session()
-
-    # Transaction interface
-    def begin(self):
-        self._txn = self.session.create_transaction()
-
-    def commit(self):
-        if self._txn is not None:
-            self._txn.commit()
-            self._txn = None
-
-    def rollback(self):
-        if self._txn is not None:
-            self._txn.rollback()
-            self._txn = None
-
-    # Interface methods
-    def get_list(self, fqdn_listname):
-        from Mailman.MailList import MailList
-        list_name, host_name = fqdn_listname.split('@', 1)
-        q = self.session.query(MailList)
-        mlists = q.select_by(list_name=list_name, host_name=host_name)
-        if mlists:
-            return mlists[0]
-        return None
-
-
-
-dbcontext = DBContext()
+    dbcontext.connect()
+    for attr in dir(dbcontext):
+        if attr.startswith('api_'):
+            exposed_name = attr[4:]
+            setattr(database, exposed_name, getattr(dbcontext, attr))

Added: branches/tmp-sqlalchemy-branch/Mailman/database/dbcontext.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/database/dbcontext.py                
                (rev 0)
+++ branches/tmp-sqlalchemy-branch/Mailman/database/dbcontext.py        
2006-12-11 12:27:47 UTC (rev 8116)
@@ -0,0 +1,137 @@
+# Copyright (C) 2006 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+import weakref
+
+from sqlalchemy import *
+from string import Template
+
+from Mailman import Version
+from Mailman.configuration import config
+from Mailman.database import address 
+from Mailman.database import listdata
+from Mailman.database import version
+from Mailman.database.txnsupport import txn
+
+
+
+class MlistRef(weakref.ref):
+    def __init__(self, mlist, callback):
+        super(MlistRef, self).__init__(mlist, callback)
+        self.fqdn_listname = mlist.fqdn_listname
+
+
+
+class DBContext(object):
+    def __init__(self):
+        self.tables = {}
+        self.metadata = None
+        self.session = None
+        # Special transaction used only for MailList.Lock() .Save() and
+        # .Unlock() interface.
+        self._mlist_txns = {}
+
+    def connect(self):
+        # Calculate the engine url
+        url = Template(config.SQLALCHEMY_ENGINE_URL).safe_substitute(
+            config.paths)
+        self.metadata = BoundMetaData(url)
+        self.metadata.engine.echo = config.SQLALCHEMY_ECHO
+        # Create all the table objects, and then let SA conditionally create
+        # them if they don't yet exist.
+        version_table = None
+        for module in (address, listdata, version):
+            table = module.make_table(self.metadata)
+            self.tables[table.name] = table
+            if module is version:
+                version_table = table
+        self.metadata.create_all()
+        # Validate schema version, updating if necessary (XXX)
+        from Mailman.interact import interact
+        r = version_table.select(version_table.c.component=='schema').execute()
+        row = r.fetchone()
+        if row is None:
+            # Database has not yet been initialized
+            version_table.insert().execute(
+                component='schema',
+                version=Version.DATABASE_SCHEMA_VERSION)
+        elif row.version <> Version.DATABASE_SCHEMA_VERSION:
+            # XXX Update schema
+            raise SchemaVersionMismatchError(row.version)
+        self.session = create_session()
+
+    # Cooperative method for use with @txn decorator
+    def _withtxn(self, meth, *args, **kws):
+        try:
+            txn = self.session.create_transaction()
+            rtn = meth(*args, **kws)
+        except:
+            txn.rollback()
+            raise
+        else:
+            txn.commit()
+            return rtn
+
+    def _unlock_mref(self, mref):
+        txn = self._mlist_txns.pop(mref.fqdn_listname, None)
+        if txn is not None:
+            txn.rollback()
+
+    # Higher level interface
+    def api_lock(self, mlist):
+        # Don't try to re-lock a list
+        if mlist.fqdn_listname in self._mlist_txns:
+            return
+        txn = self.session.create_transaction()
+        mref = MlistRef(mlist, self._unlock_mref)
+        self._mlist_txns[mlist.fqdn_listname] = txn
+
+    def api_unlock(self, mlist):
+        txn = self._mlist_txns.pop(mlist.fqdn_listname, None)
+        if txn is not None:
+            txn.rollback()
+
+    def api_save(self, mlist):
+        # When dealing with MailLists, .Save() will always be followed by
+        # .Unlock().  However lists can also be unlocked without saving.  But
+        # if it's been locked it will always be unlocked.  So the rollback in
+        # unlock will essentially be no-op'd if we've already saved the list.
+        txn = self._mlist_txns.pop(mlist.fqdn_listname, None)
+        if txn is not None:
+            txn.commit()
+
+    @txn
+    def api_add_list(self, mlist):
+        self.session.save(mlist)
+
+    @txn
+    def api_remove_list(self, mlist):
+        self.session.delete(mlist)
+
+    @txn
+    def api_find_list(self, listname, hostname):
+        from Mailman.MailList import MailList
+        q = self.session.query(MailList)
+        mlists = q.select_by(list_name=listname, host_name=hostname)
+        assert len(mlists) <= 1, 'Duplicate mailing lists!'
+        if mlists:
+            return mlists[0]
+        return None
+
+
+
+dbcontext = DBContext()

Added: branches/tmp-sqlalchemy-branch/Mailman/database/txnsupport.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/database/txnsupport.py               
                (rev 0)
+++ branches/tmp-sqlalchemy-branch/Mailman/database/txnsupport.py       
2006-12-11 12:27:47 UTC (rev 8116)
@@ -0,0 +1,34 @@
+# Copyright (C) 2006 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+# A transaction wrapping decorator. The basic idea is that methods in the
+# DBContext that need to operate on transaction boundaries can be written to
+# be transaction naive.  By wrapping them in this decorator, they
+# automatically become transaction safe.
+
+class txn(object):
+    def __init__(self, func):
+        # func is a function object, not a method (even an unbound method).
+        self._func = func
+
+    def __get__(self, obj, type=None):
+        # Return a wrapper function that creates a bound method from the
+        # function, then calls it wrapped in a transaction boundary.  Uses a
+        # non-public method called _withtxn() in the object's class.
+        def wrapper(*args, **kws):
+            return obj._withtxn(self._func.__get__(obj), *args, **kws)
+        return wrapper

Modified: branches/tmp-sqlalchemy-branch/Mailman/initialize.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/initialize.py        2006-12-07 
14:13:56 UTC (rev 8115)
+++ branches/tmp-sqlalchemy-branch/Mailman/initialize.py        2006-12-11 
12:27:47 UTC (rev 8116)
@@ -33,4 +33,4 @@
 def initialize(config=None, propagate_logs=False):
     Mailman.configuration.config.load(config)
     Mailman.loginit.initialize(propagate_logs)
-    Mailman.database.dbcontext.connect()
+    Mailman.database.initialize()

Modified: branches/tmp-sqlalchemy-branch/Mailman/testing/base.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/testing/base.py      2006-12-07 
14:13:56 UTC (rev 8115)
+++ branches/tmp-sqlalchemy-branch/Mailman/testing/base.py      2006-12-11 
12:27:47 UTC (rev 8116)
@@ -28,6 +28,7 @@
 
 from Mailman import MailList
 from Mailman import Utils
+from Mailman.bin import rmlist
 from Mailman.configuration import config
 
 NL = '\n'
@@ -37,9 +38,6 @@
 
 class TestBase(unittest.TestCase):
     def _configure(self, fp):
-##         print >> fp, \
-##               "MEMBER_ADAPTOR_CLASS = 'Mailman.SAMemberships.SAMemberships'"
-##         config.MEMBER_ADAPTOR_CLASS = 'Mailman.SAMemberships.SAMemberships'
         print >> fp, 'add_domain("example.com", "www.example.com")'
         # Only add this domain once to the current process
         if 'example.com' not in config.domains:
@@ -79,16 +77,6 @@
 
     def tearDown(self):
         self._mlist.Unlock()
-        listname = self._mlist.fqdn_listname
-        for dirtmpl in ['lists/%s',
-                        'archives/private/%s',
-                        'archives/private/%s.mbox',
-                        'archives/public/%s',
-                        'archives/public/%s.mbox',
-                        ]:
-            dir = os.path.join(config.VAR_PREFIX, dirtmpl % listname)
-            if os.path.islink(dir):
-                os.unlink(dir)
-            elif os.path.isdir(dir):
-                shutil.rmtree(dir)
+        rmlist.delete_list(self._mlist.fqdn_listname, self._mlist,
+                           archives=True, quiet=True)
         os.unlink(self._config)

Modified: branches/tmp-sqlalchemy-branch/Mailman/testing/emailbase.py
===================================================================
--- branches/tmp-sqlalchemy-branch/Mailman/testing/emailbase.py 2006-12-07 
14:13:56 UTC (rev 8115)
+++ branches/tmp-sqlalchemy-branch/Mailman/testing/emailbase.py 2006-12-11 
12:27:47 UTC (rev 8116)
@@ -59,15 +59,20 @@
 
     def setUp(self):
         TestBase.setUp(self)
-        # Second argument is ignored.
-        self._server = SinkServer(('localhost', TESTPORT), None)
         try:
+            # Second argument is ignored.
+            self._server = SinkServer(('localhost', TESTPORT), None)
+        except:
+            TestBase.tearDown(self)
+            raise
+        try:
             os.system('bin/mailmanctl -C %s -q start' % self._config)
             # If any errors occur in the above, be sure to manually call
             # tearDown().  unittest doesn't call tearDown() for errors in
             # setUp().
         except:
             self.tearDown()
+            raise
 
     def tearDown(self):
         os.system('bin/mailmanctl -C %s -q stop' % self._config)


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to