diff options
| -rw-r--r-- | Mailman/database/__init__.py | 12 | ||||
| -rw-r--r-- | Mailman/database/model/roster.py | 19 | ||||
| -rw-r--r-- | Mailman/database/model/user.py | 8 | ||||
| -rw-r--r-- | Mailman/database/usermanager.py | 21 | ||||
| -rw-r--r-- | Mailman/docs/addresses.txt | 86 | ||||
| -rw-r--r-- | Mailman/docs/mlist-addresses.txt | 30 | ||||
| -rw-r--r-- | Mailman/interfaces/database.py | 3 | ||||
| -rw-r--r-- | Mailman/tests/test_documentation.py | 2 |
8 files changed, 85 insertions, 96 deletions
diff --git a/Mailman/database/__init__.py b/Mailman/database/__init__.py index a3a5293af..78b118be6 100644 --- a/Mailman/database/__init__.py +++ b/Mailman/database/__init__.py @@ -20,7 +20,6 @@ from __future__ import with_statement __metaclass__ = type __all__ = [ 'StockDatabase', - 'flush', # for test convenience ] import os @@ -34,20 +33,12 @@ from Mailman.database.listmanager import ListManager from Mailman.database.usermanager import UserManager from Mailman.database.messagestore import MessageStore -# Test suite convenience. Application code should use config.db.flush() -# instead. -flush = None - class StockDatabase: implements(IDatabase) def __init__(self): - # Expose the flush() method for test case convenience using the stock - # database. - global flush - flush = self.flush self.list_manager = None self.user_manager = None self.message_store = None @@ -71,9 +62,6 @@ class StockDatabase: self.pendings = Pendings() self.requests = Requests() - def flush(self): - pass - def _reset(self): for model_class in _class_registry: for row in self.store.find(model_class): diff --git a/Mailman/database/model/roster.py b/Mailman/database/model/roster.py index ab228d762..0c8c3bd87 100644 --- a/Mailman/database/model/roster.py +++ b/Mailman/database/model/roster.py @@ -22,6 +22,7 @@ the ones that fit a particular role. These are used as the member, owner, moderator, and administrator roster filters. """ +from storm.locals import * from zope.interface import implements from Mailman.configuration import config @@ -49,7 +50,8 @@ class AbstractRoster(object): @property def members(self): - for member in Member.query.filter_by( + for member in config.db.store.find( + Member, mailing_list=self._mlist.fqdn_listname, role=self.role): yield member @@ -122,10 +124,11 @@ class AdministratorRoster(AbstractRoster): def members(self): # Administrators are defined as the union of the owners and the # moderators. - members = Member.query.filter( - and_(Member.c.mailing_list == self._mlist.fqdn_listname, - or_(Member.c.role == MemberRole.owner, - Member.c.role == MemberRole.moderator))) + members = config.db.store.find( + Member, + Member.mailing_list == self._mlist.fqdn_listname, + Or(Member.role == MemberRole.owner, + Member.role == MemberRole.moderator)) for member in members: yield member @@ -156,7 +159,8 @@ class RegularMemberRoster(AbstractRoster): # Query for all the Members which have a role of MemberRole.member and # are subscribed to this mailing list. Then return only those members # that have a regular delivery mode. - for member in Member.query.filter_by( + for member in config.db.store.find( + Member, mailing_list=self._mlist.fqdn_listname, role=MemberRole.member): if member.delivery_mode == DeliveryMode.regular: @@ -182,7 +186,8 @@ class DigestMemberRoster(AbstractRoster): # Query for all the Members which have a role of MemberRole.member and # are subscribed to this mailing list. Then return only those members # that have one of the digest delivery modes. - for member in Member.query.filter_by( + for member in config.db.store.find( + Member, mailing_list=self._mlist.fqdn_listname, role=MemberRole.member): if member.delivery_mode in _digest_modes: diff --git a/Mailman/database/model/user.py b/Mailman/database/model/user.py index 7be54c3c4..2d87b9648 100644 --- a/Mailman/database/model/user.py +++ b/Mailman/database/model/user.py @@ -20,6 +20,7 @@ from storm.locals import * from zope.interface import implements from Mailman import Errors +from Mailman.configuration import config from Mailman.database import Model from Mailman.database.model import Address from Mailman.database.model import Preferences @@ -52,8 +53,11 @@ class User(Model): address.user = None def controls(self, address): - found = Address.get_by(address=address) - return bool(found and found.user is self) + found = config.db.store.find(Address, address=address) + if found.count() == 0: + return False + assert found.count() == 1, 'Unexpected count' + return found[0].user is self def register(self, address, real_name=None): # First, see if the address already exists diff --git a/Mailman/database/usermanager.py b/Mailman/database/usermanager.py index 6fecfcc8b..f58e52e68 100644 --- a/Mailman/database/usermanager.py +++ b/Mailman/database/usermanager.py @@ -54,7 +54,9 @@ class UserManager(object): yield user def get_user(self, address): - addresses = Address.query.filter_by(address=address.lower()) + # Avoid circular imports. + from Mailman.database.model import Address + addresses = config.db.store.find(Address, address=address.lower()) if addresses.count() == 0: return None elif addresses.count() == 1: @@ -63,17 +65,20 @@ class UserManager(object): raise AssertionError('Unexpected query count') def create_address(self, address, real_name=None): - addresses = Address.query.filter_by(address=address.lower()) + # Avoid circular imports. + from Mailman.database.model import Address, Preferences + addresses = config.db.store.find(Address, address=address.lower()) if addresses.count() == 1: found = addresses[0] raise Errors.ExistingAddressError(found.original_address) assert addresses.count() == 0, 'Unexpected results' if real_name is None: - real_name = '' + real_name = u'' # It's okay not to lower case the 'address' argument because the # constructor will do the right thing. address = Address(address, real_name) address.preferences = Preferences() + config.db.store.add(address) return address def delete_address(self, address): @@ -81,10 +86,12 @@ class UserManager(object): # unlinked before the address can be deleted. if address.user: address.user.unlink(address) - address.delete() + config.db.store.remove(address) def get_address(self, address): - addresses = Address.query.filter_by(address=address.lower()) + # Avoid circular imports. + from Mailman.database.model import Address + addresses = config.db.store.find(Address, address=address.lower()) if addresses.count() == 0: return None elif addresses.count() == 1: @@ -94,5 +101,7 @@ class UserManager(object): @property def addresses(self): - for address in Address.query.filter_by().all(): + # Avoid circular imports. + from Mailman.database.model.address import Address + for address in config.db.store.find(Address): yield address diff --git a/Mailman/docs/addresses.txt b/Mailman/docs/addresses.txt index 4dd8b44ad..0439eaf33 100644 --- a/Mailman/docs/addresses.txt +++ b/Mailman/docs/addresses.txt @@ -6,7 +6,6 @@ those addresses, such as their registration date, and whether and when they've been validated. Addresses may be linked to the users that Mailman knows about. Addresses are subscribed to mailing lists though members. - >>> from Mailman.database import flush >>> from Mailman.configuration import config >>> usermgr = config.db.user_manager @@ -22,24 +21,23 @@ no addresses. Creating an unlinked email address is straightforward. - >>> address_1 = usermgr.create_address('aperson@example.com') - >>> flush() + >>> address_1 = usermgr.create_address(u'aperson@example.com') >>> sorted(address.address for address in usermgr.addresses) - ['aperson@example.com'] + [u'aperson@example.com'] However, such addresses have no real name. >>> address_1.real_name - '' + u'' You can also create an email address object with a real name. - >>> address_2 = usermgr.create_address('bperson@example.com', 'Ben Person') - >>> flush() + >>> address_2 = usermgr.create_address( + ... u'bperson@example.com', u'Ben Person') >>> sorted(address.address for address in usermgr.addresses) - ['aperson@example.com', 'bperson@example.com'] + [u'aperson@example.com', u'bperson@example.com'] >>> sorted(address.real_name for address in usermgr.addresses) - ['', 'Ben Person'] + [u'', u'Ben Person'] The str() of the address is the RFC 2822 preferred originator format, while the repr() carries more information. @@ -51,38 +49,36 @@ the repr() carries more information. You can assign real names to existing addresses. - >>> address_1.real_name = 'Anne Person' - >>> flush() + >>> address_1.real_name = u'Anne Person' >>> sorted(address.real_name for address in usermgr.addresses) - ['Anne Person', 'Ben Person'] + [u'Anne Person', u'Ben Person'] These addresses are not linked to users, and can be seen by searching the user manager for an associated user. - >>> print usermgr.get_user('aperson@example.com') + >>> print usermgr.get_user(u'aperson@example.com') None - >>> print usermgr.get_user('bperson@example.com') + >>> print usermgr.get_user(u'bperson@example.com') None You can create email addresses that are linked to users by using a different interface. - >>> user_1 = usermgr.create_user('cperson@example.com', 'Claire Person') + >>> user_1 = usermgr.create_user(u'cperson@example.com', u'Claire Person') >>> sorted(address.address for address in user_1.addresses) - ['cperson@example.com'] - >>> flush() + [u'cperson@example.com'] >>> sorted(address.address for address in usermgr.addresses) - ['aperson@example.com', 'bperson@example.com', 'cperson@example.com'] + [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com'] >>> sorted(address.real_name for address in usermgr.addresses) - ['Anne Person', 'Ben Person', 'Claire Person'] + [u'Anne Person', u'Ben Person', u'Claire Person'] And now you can find the associated user. - >>> print usermgr.get_user('aperson@example.com') + >>> print usermgr.get_user(u'aperson@example.com') None - >>> print usermgr.get_user('bperson@example.com') + >>> print usermgr.get_user(u'bperson@example.com') None - >>> usermgr.get_user('cperson@example.com') + >>> usermgr.get_user(u'cperson@example.com') <User "Claire Person" at ...> @@ -92,28 +88,26 @@ Deleting addresses You can remove an unlinked address from the user manager. >>> usermgr.delete_address(address_1) - >>> flush() >>> sorted(address.address for address in usermgr.addresses) - ['bperson@example.com', 'cperson@example.com'] + [u'bperson@example.com', u'cperson@example.com'] >>> sorted(address.real_name for address in usermgr.addresses) - ['Ben Person', 'Claire Person'] + [u'Ben Person', u'Claire Person'] Deleting a linked address does not delete the user, but it does unlink the address from the user. >>> sorted(address.address for address in user_1.addresses) - ['cperson@example.com'] - >>> user_1.controls('cperson@example.com') + [u'cperson@example.com'] + >>> user_1.controls(u'cperson@example.com') True >>> address_3 = list(user_1.addresses)[0] >>> usermgr.delete_address(address_3) - >>> flush() >>> sorted(address.address for address in user_1.addresses) [] - >>> user_1.controls('cperson@example.com') + >>> user_1.controls(u'cperson@example.com') False >>> sorted(address.address for address in usermgr.addresses) - ['bperson@example.com'] + [u'bperson@example.com'] Registration and validation @@ -122,8 +116,8 @@ Registration and validation Addresses have two dates, the date the address was registered on and the date the address was validated on. Neither date is set by default. - >>> address_4 = usermgr.create_address('dperson@example.com', 'Dan Person') - >>> flush() + >>> address_4 = usermgr.create_address( + ... u'dperson@example.com', u'Dan Person') >>> print address_4.registered_on None >>> print address_4.verified_on @@ -133,7 +127,6 @@ The registered date takes a Python datetime object. >>> from datetime import datetime >>> address_4.registered_on = datetime(2007, 5, 8, 22, 54, 1) - >>> flush() >>> print address_4.registered_on 2007-05-08 22:54:01 >>> print address_4.verified_on @@ -142,7 +135,6 @@ The registered date takes a Python datetime object. And of course, you can also set the validation date. >>> address_4.verified_on = datetime(2007, 5, 13, 22, 54, 1) - >>> flush() >>> print address_4.registered_on 2007-05-08 22:54:01 >>> print address_4.verified_on @@ -156,8 +148,8 @@ Addresses get subscribed to mailing lists, not users. When the address is subscribed, a role is specified. >>> address_5 = usermgr.create_address( - ... 'eperson@example.com', 'Elly Person') - >>> mlist = config.db.list_manager.create('_xtext@example.com') + ... u'eperson@example.com', u'Elly Person') + >>> mlist = config.db.list_manager.create(u'_xtext@example.com') >>> from Mailman.interfaces import MemberRole >>> address_5.subscribe(mlist, MemberRole.owner) <Member: Elly Person <eperson@example.com> on @@ -165,7 +157,6 @@ subscribed, a role is specified. >>> address_5.subscribe(mlist, MemberRole.member) <Member: Elly Person <eperson@example.com> on _xtext@example.com as MemberRole.member> - >>> flush() Now Elly is both an owner and a member of the mailing list. @@ -196,8 +187,7 @@ when sending the user a message, but it treats addresses that are different in case equivalently in all other situations. >>> address_6 = usermgr.create_address( - ... 'FPERSON@example.com', 'Frank Person') - >>> flush() + ... u'FPERSON@example.com', u'Frank Person') The str() of such an address prints the RFC 2822 preferred originator format with the original case-preserved address. The repr() contains all the gory @@ -213,22 +203,22 @@ Both the case-insensitive version of the address and the original case-preserved version are available on attributes of the IAddress object. >>> address_6.address - 'fperson@example.com' + u'fperson@example.com' >>> address_6.original_address - 'FPERSON@example.com' + u'FPERSON@example.com' Because addresses are case-insensitive for all other purposes, you cannot create an address that differs only in case. - >>> usermgr.create_address('fperson@example.com') + >>> usermgr.create_address(u'fperson@example.com') Traceback (most recent call last): ... ExistingAddressError: FPERSON@example.com - >>> usermgr.create_address('fperson@EXAMPLE.COM') + >>> usermgr.create_address(u'fperson@EXAMPLE.COM') Traceback (most recent call last): ... ExistingAddressError: FPERSON@example.com - >>> usermgr.create_address('FPERSON@example.com') + >>> usermgr.create_address(u'FPERSON@example.com') Traceback (most recent call last): ... ExistingAddressError: FPERSON@example.com @@ -236,7 +226,7 @@ create an address that differs only in case. You can get the address using either the lower cased version or case-preserved version. In fact, searching for an address is case insensitive. - >>> usermgr.get_address('fperson@example.com').address - 'fperson@example.com' - >>> usermgr.get_address('FPERSON@example.com').address - 'fperson@example.com' + >>> usermgr.get_address(u'fperson@example.com').address + u'fperson@example.com' + >>> usermgr.get_address(u'FPERSON@example.com').address + u'fperson@example.com' diff --git a/Mailman/docs/mlist-addresses.txt b/Mailman/docs/mlist-addresses.txt index 2eba70f8f..dc2184175 100644 --- a/Mailman/docs/mlist-addresses.txt +++ b/Mailman/docs/mlist-addresses.txt @@ -5,56 +5,54 @@ Every mailing list has a number of addresses which are publicly available. These are defined in the IMailingListAddresses interface. >>> from Mailman.configuration import config - >>> from Mailman.database import flush - >>> mlist = config.db.list_manager.create('_xtest@example.com') - >>> flush() + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') The posting address is where people send messages to be posted to the mailing list. This is exactly the same as the fully qualified list name. >>> mlist.fqdn_listname - '_xtest@example.com' + u'_xtest@example.com' >>> mlist.posting_address - '_xtest@example.com' + u'_xtest@example.com' Messages to the mailing list's 'no reply' address always get discarded without prejudice. >>> mlist.noreply_address - 'noreply@example.com' + u'noreply@example.com' The mailing list's owner address reaches the human moderators. >>> mlist.owner_address - '_xtest-owner@example.com' + u'_xtest-owner@example.com' The request address goes to the list's email command robot. >>> mlist.request_address - '_xtest-request@example.com' + u'_xtest-request@example.com' The bounces address accepts and processes all potential bounces. >>> mlist.bounces_address - '_xtest-bounces@example.com' + u'_xtest-bounces@example.com' The join (a.k.a. subscribe) address is where someone can email to get added to the mailing list. The subscribe alias is a synonym for join, but it's deprecated. >>> mlist.join_address - '_xtest-join@example.com' + u'_xtest-join@example.com' >>> mlist.subscribe_address - '_xtest-subscribe@example.com' + u'_xtest-subscribe@example.com' The leave (a.k.a. unsubscribe) address is where someone can email to get added to the mailing list. The unsubscribe alias is a synonym for leave, but it's deprecated. >>> mlist.leave_address - '_xtest-leave@example.com' + u'_xtest-leave@example.com' >>> mlist.unsubscribe_address - '_xtest-unsubscribe@example.com' + u'_xtest-unsubscribe@example.com' Email confirmations @@ -66,12 +64,12 @@ included in the local part of the email address. The exact format of this is dependent on the VERP_CONFIRM_FORMAT configuration variable. >>> mlist.confirm_address('cookie') - '_xtest-confirm+cookie@example.com' + u'_xtest-confirm+cookie@example.com' >>> mlist.confirm_address('wookie') - '_xtest-confirm+wookie@example.com' + u'_xtest-confirm+wookie@example.com' >>> old_format = config.VERP_CONFIRM_FORMAT >>> config.VERP_CONFIRM_FORMAT = '$address---$cookie' >>> mlist.confirm_address('cookie') - '_xtest-confirm---cookie@example.com' + u'_xtest-confirm---cookie@example.com' >>> config.VERP_CONFIRM_FORMAT = old_format diff --git a/Mailman/interfaces/database.py b/Mailman/interfaces/database.py index f4dd693a4..372992952 100644 --- a/Mailman/interfaces/database.py +++ b/Mailman/interfaces/database.py @@ -39,9 +39,6 @@ class IDatabase(Interface): configuration file setting. """ - def flush(): - """Flush current database changes.""" - def _reset(): """Reset the database to its pristine state. diff --git a/Mailman/tests/test_documentation.py b/Mailman/tests/test_documentation.py index 9de74e109..575d8e6bd 100644 --- a/Mailman/tests/test_documentation.py +++ b/Mailman/tests/test_documentation.py @@ -29,7 +29,6 @@ import Mailman from Mailman.Message import Message from Mailman.app.styles import style_manager from Mailman.configuration import config -from Mailman.database import flush COMMASPACE = ', ' @@ -51,7 +50,6 @@ def cleaning_teardown(testobj): """Clear all persistent data at the end of a doctest.""" # Clear the database of all rows. config.db._reset() - flush() # Remove all but the default style. for style in style_manager.styles: if style.name <> 'default': |
