diff options
| author | Barry Warsaw | 2008-02-19 21:22:20 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2008-02-19 21:22:20 -0500 |
| commit | aab29f252ebefb1520714080a90bb42a25393f18 (patch) | |
| tree | a5df0c60d606bd3fbe1455d9a6c95be5dd55e6f6 | |
| parent | 2bd332002f241ab7ca77f935a73692f6cbf41fe5 (diff) | |
| download | mailman-aab29f252ebefb1520714080a90bb42a25393f18.tar.gz mailman-aab29f252ebefb1520714080a90bb42a25393f18.tar.zst mailman-aab29f252ebefb1520714080a90bb42a25393f18.zip | |
| -rw-r--r-- | Mailman/Errors.py | 54 | ||||
| -rw-r--r-- | Mailman/app/styles.py | 16 | ||||
| -rw-r--r-- | Mailman/bin/newlist.py | 4 | ||||
| -rw-r--r-- | Mailman/database/__init__.py | 3 | ||||
| -rw-r--r-- | Mailman/database/listmanager.py | 19 | ||||
| -rw-r--r-- | Mailman/database/user.py | 16 | ||||
| -rw-r--r-- | Mailman/database/usermanager.py | 5 | ||||
| -rw-r--r-- | Mailman/docs/listmanager.txt | 2 | ||||
| -rw-r--r-- | Mailman/interfaces/address.py | 20 | ||||
| -rw-r--r-- | Mailman/interfaces/database.py | 20 | ||||
| -rw-r--r-- | Mailman/interfaces/errors.py | 28 | ||||
| -rw-r--r-- | Mailman/interfaces/listmanager.py | 52 | ||||
| -rw-r--r-- | Mailman/interfaces/styles.py | 32 | ||||
| -rw-r--r-- | Mailman/interfaces/usermanager.py | 5 | ||||
| -rw-r--r-- | setup.py | 1 |
15 files changed, 176 insertions, 101 deletions
diff --git a/Mailman/Errors.py b/Mailman/Errors.py index c0d5ced72..1c8b215c5 100644 --- a/Mailman/Errors.py +++ b/Mailman/Errors.py @@ -36,9 +36,6 @@ class MMUnknownListError(MMListError): def __str__(self): return self._listname -class MMCorruptListDatabaseError(MMListError): pass -class MMListNotReadyError(MMListError): pass -class MMListAlreadyExistsError(MMListError): pass # Membership exceptions class MMMemberError(MailmanException): pass @@ -189,22 +186,6 @@ class AlreadySubscribedError(SubscriptionError): -# Database exceptions -class DatabaseError(MailmanError): - """A problem with the database occurred.""" - - -class SchemaVersionMismatchError(DatabaseError): - def __init__(self, got): - self._got = got - - def __str__(self): - from Mailman.Version import DATABASE_SCHEMA_VERSION - return 'Incompatible database schema version (got: %d, expected: %d)' \ - % (self._got, DATABASE_SCHEMA_VERSION) - - - class PasswordError(MailmanError): """A password related error.""" @@ -225,38 +206,3 @@ class BadPasswordSchemeError(PasswordError): def __str__(self): return 'A bad password scheme was given: %s' % self.scheme_name - - - -class UserError(MailmanError): - """A general user-related error occurred.""" - - -class RosterError(UserError): - """A roster-related error occurred.""" - - -class RosterExistsError(RosterError): - """The named roster already exists.""" - - - -class AddressError(MailmanError): - """A general address-related error occurred.""" - - -class ExistingAddressError(AddressError): - """The given email address already exists.""" - - -class AddressAlreadyLinkedError(AddressError): - """The address is already linked to a user.""" - - -class AddressNotLinkedError(AddressError): - """The address is not linked to the user.""" - - - -class DuplicateStyleError(MailmanError): - """A style with the same name is already registered.""" diff --git a/Mailman/app/styles.py b/Mailman/app/styles.py index 92c8bd39b..5c7d2351b 100644 --- a/Mailman/app/styles.py +++ b/Mailman/app/styles.py @@ -30,22 +30,25 @@ from zope.interface import implements from zope.interface.verify import verifyObject from Mailman import Utils -from Mailman.Errors import DuplicateStyleError from Mailman.app.plugins import get_plugins from Mailman.configuration import config from Mailman.i18n import _ from Mailman.interfaces import ( - Action, IStyle, IStyleManager, NewsModeration, Personalization) + Action, DuplicateStyleError, IStyle, IStyleManager, NewsModeration, + Personalization) class DefaultStyle: + """The defalt (i.e. legacy) style.""" + implements(IStyle) name = 'default' priority = 0 # the lowest priority style def apply(self, mailing_list): + """See `IStyle`.""" # For cut-n-paste convenience. mlist = mailing_list # Most of these were ripped from the old MailList.InitVars() method. @@ -233,6 +236,7 @@ class DefaultStyle: mlist.pipeline = u'built-in' def match(self, mailing_list, styles): + """See `IStyle`.""" # If no other styles have matched, then the default style matches. if len(styles) == 0: styles.append(self) @@ -240,9 +244,12 @@ class DefaultStyle: class StyleManager: + """The built-in style manager.""" + implements(IStyleManager) def __init__(self): + """Install all styles from registered plugins, and install them.""" self._styles = {} # Install all the styles provided by plugins. for style_factory in get_plugins('mailman.styles'): @@ -251,9 +258,11 @@ class StyleManager: self.register(style) def get(self, name): + """See `IStyleManager`.""" return self._styles.get(name) def lookup(self, mailing_list): + """See `IStyleManager`.""" matched_styles = [] for style in self.styles: style.match(mailing_list, matched_styles) @@ -262,18 +271,21 @@ class StyleManager: @property def styles(self): + """See `IStyleManager`.""" for style in sorted(self._styles.values(), key=attrgetter('priority'), reverse=True): yield style def register(self, style): + """See `IStyleManager`.""" verifyObject(IStyle, style) if style.name in self._styles: raise DuplicateStyleError(style.name) self._styles[style.name] = style def unregister(self, style): + """See `IStyleManager`.""" # Let KeyErrors percolate up. del self._styles[style.name] diff --git a/Mailman/bin/newlist.py b/Mailman/bin/newlist.py index f63e46724..45373510e 100644 --- a/Mailman/bin/newlist.py +++ b/Mailman/bin/newlist.py @@ -31,6 +31,8 @@ from Mailman import i18n from Mailman.app.lifecycle import create_list from Mailman.configuration import config from Mailman.initialize import initialize +from Mailman.interfaces import ListAlreadyExistsError + _ = i18n._ @@ -107,7 +109,7 @@ def main(): mlist.preferred_language = opts.language except Errors.InvalidEmailAddress: parser.error(_('Illegal list name: $fqdn_listname')) - except Errors.MMListAlreadyExistsError: + except ListAlreadyExistsError: parser.error(_('List already exists: $fqdn_listname')) except Errors.BadDomainSpecificationError, domain: parser.error(_('Undefined domain: $domain')) diff --git a/Mailman/database/__init__.py b/Mailman/database/__init__.py index f6fda8494..7ca60f145 100644 --- a/Mailman/database/__init__.py +++ b/Mailman/database/__init__.py @@ -31,7 +31,6 @@ from string import Template from urlparse import urlparse from zope.interface import implements -from Mailman.Errors import SchemaVersionMismatchError from Mailman.configuration import config from Mailman.database.listmanager import ListManager from Mailman.database.messagestore import MessageStore @@ -39,7 +38,7 @@ from Mailman.database.pending import Pendings from Mailman.database.requests import Requests from Mailman.database.usermanager import UserManager from Mailman.database.version import Version -from Mailman.interfaces import IDatabase +from Mailman.interfaces import IDatabase, SchemaVersionMismatchError diff --git a/Mailman/database/listmanager.py b/Mailman/database/listmanager.py index a79cbf565..7b02767c7 100644 --- a/Mailman/database/listmanager.py +++ b/Mailman/database/listmanager.py @@ -25,30 +25,31 @@ from Mailman import Errors from Mailman.Utils import split_listname, fqdn_listname from Mailman.configuration import config from Mailman.database.mailinglist import MailingList -from Mailman.interfaces import IListManager +from Mailman.interfaces import IListManager, ListAlreadyExistsError class ListManager(object): + """An implementation of the `IListManager` interface.""" + implements(IListManager) def create(self, fqdn_listname): + """See `IListManager`.""" listname, hostname = split_listname(fqdn_listname) mlist = config.db.store.find( MailingList, MailingList.list_name == listname, MailingList.host_name == hostname).one() if mlist: - raise Errors.MMListAlreadyExistsError(fqdn_listname) + raise ListAlreadyExistsError(fqdn_listname) mlist = MailingList(fqdn_listname) mlist.created_at = datetime.datetime.now() config.db.store.add(mlist) return mlist - def delete(self, mlist): - config.db.store.remove(mlist) - def get(self, fqdn_listname): + """See `IListManager`.""" listname, hostname = split_listname(fqdn_listname) mlist = config.db.store.find(MailingList, list_name=listname, @@ -58,14 +59,18 @@ class ListManager(object): mlist._restore() return mlist + def delete(self, mlist): + """See `IListManager`.""" + config.db.store.remove(mlist) + @property def mailing_lists(self): - # Don't forget, the MailingList objects that this class manages must - # be wrapped in a MailList object as expected by this interface. + """See `IListManager`.""" for fqdn_listname in self.names: yield self.get(fqdn_listname) @property def names(self): + """See `IListManager`.""" for mlist in config.db.store.find(MailingList): yield fqdn_listname(mlist.list_name, mlist.host_name) diff --git a/Mailman/database/user.py b/Mailman/database/user.py index d9c87ada0..65a3c92f8 100644 --- a/Mailman/database/user.py +++ b/Mailman/database/user.py @@ -19,16 +19,18 @@ from email.utils import formataddr from storm.locals import * from zope.interface import implements -from Mailman import Errors from Mailman.configuration import config from Mailman.database.model import Model from Mailman.database.address import Address from Mailman.database.preferences import Preferences -from Mailman.interfaces import IUser +from Mailman.interfaces import ( + AddressAlreadyLinkedError, AddressNotLinkedError, IUser) class User(Model): + """Mailman users.""" + implements(IUser) id = Int(primary=True) @@ -43,16 +45,19 @@ class User(Model): return '<User "%s" at %#x>' % (self.real_name, id(self)) def link(self, address): + """See `IUser`.""" if address.user is not None: - raise Errors.AddressAlreadyLinkedError(address) + raise AddressAlreadyLinkedError(address) address.user = self def unlink(self, address): + """See `IUser`.""" if address.user is None: - raise Errors.AddressNotLinkedError(address) + raise AddressNotLinkedError(address) address.user = None def controls(self, address): + """See `IUser`.""" found = config.db.store.find(Address, address=address) if found.count() == 0: return False @@ -60,6 +65,7 @@ class User(Model): return found[0].user is self def register(self, address, real_name=None): + """See `IUser`.""" # First, see if the address already exists addrobj = config.db.store.find(Address, address=address).one() if addrobj is None: @@ -69,6 +75,6 @@ class User(Model): addrobj.preferences = Preferences() # Link the address to the user if it is not already linked. if addrobj.user is not None: - raise Errors.AddressAlreadyLinkedError(addrobj) + raise AddressAlreadyLinkedError(addrobj) addrobj.user = self return addrobj diff --git a/Mailman/database/usermanager.py b/Mailman/database/usermanager.py index d5b7003dc..315e362d7 100644 --- a/Mailman/database/usermanager.py +++ b/Mailman/database/usermanager.py @@ -23,12 +23,11 @@ import os from zope.interface import implements -from Mailman import Errors from Mailman.configuration import config from Mailman.database.address import Address from Mailman.database.preferences import Preferences from Mailman.database.user import User -from Mailman.interfaces import IUserManager +from Mailman.interfaces import ExistingAddressError, IUserManager @@ -67,7 +66,7 @@ class UserManager(object): addresses = config.db.store.find(Address, address=address.lower()) if addresses.count() == 1: found = addresses[0] - raise Errors.ExistingAddressError(found.original_address) + raise ExistingAddressError(found.original_address) assert addresses.count() == 0, 'Unexpected results' if real_name is None: real_name = u'' diff --git a/Mailman/docs/listmanager.txt b/Mailman/docs/listmanager.txt index 832100aca..7a5c78df9 100644 --- a/Mailman/docs/listmanager.txt +++ b/Mailman/docs/listmanager.txt @@ -40,7 +40,7 @@ you will get an exception. >>> mlist_dup = listmgr.create(u'_xtest@example.com') Traceback (most recent call last): ... - MMListAlreadyExistsError: _xtest@example.com + ListAlreadyExistsError: _xtest@example.com Deleting a mailing list diff --git a/Mailman/interfaces/address.py b/Mailman/interfaces/address.py index c843ce4eb..bbc291a64 100644 --- a/Mailman/interfaces/address.py +++ b/Mailman/interfaces/address.py @@ -19,6 +19,26 @@ from zope.interface import Interface, Attribute +from Mailman.interfaces.errors import MailmanError + + + +class AddressError(MailmanError): + """A general address-related error occurred.""" + + +class ExistingAddressError(AddressError): + """The given email address already exists.""" + + +class AddressAlreadyLinkedError(AddressError): + """The address is already linked to a user.""" + + +class AddressNotLinkedError(AddressError): + """The address is not linked to the user.""" + + class IAddress(Interface): diff --git a/Mailman/interfaces/database.py b/Mailman/interfaces/database.py index 8770839af..c969e9ff5 100644 --- a/Mailman/interfaces/database.py +++ b/Mailman/interfaces/database.py @@ -25,6 +25,26 @@ Mailman's back end. from zope.interface import Interface, Attribute +from Mailman.Version import DATABASE_SCHEMA_VERSION +from Mailman.interfaces.errors import MailmanError + + + +class DatabaseError(MailmanError): + """A problem with the database occurred.""" + + +class SchemaVersionMismatchError(DatabaseError): + """The database schema version number did not match what was expected.""" + + def __init__(self, got): + self._got = got + + def __str__(self): + return ( + 'Incompatible database schema version (got: %d, expected: %d)' + % (self._got, DATABASE_SCHEMA_VERSION)) + class IDatabase(Interface): diff --git a/Mailman/interfaces/errors.py b/Mailman/interfaces/errors.py new file mode 100644 index 000000000..81f03d1ea --- /dev/null +++ b/Mailman/interfaces/errors.py @@ -0,0 +1,28 @@ +# Copyright (C) 1998-2008 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. + +"""Base Mailman exceptions. + +The exceptions in this module are those that are commonly shared among many +components. More specific exceptions will be located in the relevant +interfaces. +""" + + + +class MailmanError(Exception): + """Base class for all Mailman exceptions.""" diff --git a/Mailman/interfaces/listmanager.py b/Mailman/interfaces/listmanager.py index 0a3b7043e..38321f3ba 100644 --- a/Mailman/interfaces/listmanager.py +++ b/Mailman/interfaces/listmanager.py @@ -17,44 +17,64 @@ """Interface for list storage, deleting, and finding.""" +__metaclass__ = type +__all__ = [ + 'IListManager', + 'ListAlreadyExistsError', + ] + + from zope.interface import Interface, Attribute +from Mailman.interfaces.errors import MailmanError + + + +class ListAlreadyExistsError(MailmanError): + """Attempted to create a mailing list that already exists. + + Mailing list objects must be uniquely named by their fully qualified list + name. + """ class IListManager(Interface): """The interface of the global list manager. - The list manager manages IMailingList objects. You can add and remove - IMailingList objects from the list manager, and you can retrieve them - from the manager via their fully qualified list name - (e.g. 'mylist@example.com'). + The list manager manages `IMailingList` objects. You can add and remove + `IMailingList` objects from the list manager, and you can retrieve them + from the manager via their fully qualified list name, e.g.: + `mylist@example.com`. """ def create(fqdn_listname): - """Create an IMailingList with the given fully qualified list name. + """Create a mailing list with the given name. - Raises MMListAlreadyExistsError if the named list already exists. + :type fqdn_listname: Unicode + :param fqdn_listname: The fully qualified name of the mailing list, + e.g. `mylist@example.com`. + :return: The newly created `IMailingList`. + :raise `ListAlreadyExistsError` if the named list already exists. """ def get(fqdn_listname): - """Return the IMailingList with the given fully qualified list name. + """Return the mailing list with the given name, if it exists. - Raises MMUnknownListError if the names list does not exist. + :type fqdn_listname: Unicode. + :param fqdn_listname: The fully qualified name of the mailing list. + :return: the matching `IMailingList` or None if the named list does + not exist. """ def delete(mlist): - """Remove the IMailingList from the backend storage.""" - - def get(fqdn_listname): - """Find the IMailingList with the matching fully qualified list name. + """Remove the mailing list from the database. - :param fqdn_listname: Fully qualified list name to get. - :return: The matching IMailingList or None if there was no such - matching mailing list. + :type mlist: `IMailingList` + :param mlist: The mailing list to delete. """ mailing_lists = Attribute( - """An iterator over all the IMailingList objects managed by this list + """An iterator over all the mailing list objects managed by this list manager.""") names = Attribute( diff --git a/Mailman/interfaces/styles.py b/Mailman/interfaces/styles.py index 49084ecbf..aaa88e330 100644 --- a/Mailman/interfaces/styles.py +++ b/Mailman/interfaces/styles.py @@ -17,7 +17,21 @@ """Interfaces for list styles.""" +__metaclass__ = type +__all__ = [ + 'DuplicateStyleError', + 'IStyle', + 'IStyleManager', + ] + + from zope.interface import Interface, Attribute +from Mailman.interfaces.errors import MailmanError + + + +class DuplicateStyleError(MailmanError): + """A style with the same name is already registered.""" @@ -33,7 +47,8 @@ class IStyle(Interface): def apply(mailing_list): """Apply the style to the mailing list. - :param mailing_list: an IMailingList. + :type mailing_list: `IMailingList`. + :param mailing_list: the mailing list to apply the style to. """ def match(mailing_list, styles): @@ -43,8 +58,9 @@ class IStyle(Interface): the style may append itself to the `styles` list. This list will be ordered when returned from `IStyleManager.lookup()`. - :param mailing_list: an IMailingList. - :param styles: ordered list of IStyles matched so far. + :type mailing_list: `IMailingList`. + :param mailing_list: the mailing list object. + :param styles: ordered list of `IStyles` matched so far. """ @@ -55,20 +71,22 @@ class IStyleManager(Interface): def get(name): """Return the named style or None. + :type name: Unicode :param name: A style name. - :return: an IStyle or None. + :return: the named `IStyle` or None if the style doesn't exist. """ def lookup(mailing_list): """Return a list of styles for the given mailing list. - Use various registered rules to find an IStyle for the given mailing + Use various registered rules to find an `IStyle` for the given mailing list. The returned styles are ordered by their priority. Style matches can be registered and reordered by plugins. - :param mailing_list: An IMailingList. - :return: ordered list of IStyles. Zero is the lowest priority. + :type mailing_list: `IMailingList`. + :param mailing_list: The mailing list object to find a style for. + :return: ordered list of `IStyles`. Zero is the lowest priority. """ styles = Attribute( diff --git a/Mailman/interfaces/usermanager.py b/Mailman/interfaces/usermanager.py index a2f912e60..702370593 100644 --- a/Mailman/interfaces/usermanager.py +++ b/Mailman/interfaces/usermanager.py @@ -38,8 +38,9 @@ class IUserManager(Interface): When address is given, an IAddress is also created and linked to the new IUser object. If the address already exists, an - ExistingAddressError is raised. If the address exists but is already - linked to another user, an AddressAlreadyLinkedError is raised. + `ExistingAddressError` is raised. If the address exists but is + already linked to another user, an AddressAlreadyLinkedError is + raised. When real_name is given, the IUser's real_name is set to this string. If an IAddress is also created and linked, its real_name is set to the @@ -92,7 +92,6 @@ Any other spelling is incorrect.""", 'mailman.rules' : 'default = Mailman.rules:initialize', 'mailman.handlers' : 'default = Mailman.pipeline:initialize', }, - # Third-party requirements. install_requires = [ 'locknix', 'munepy', |
