launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #24996
[Merge] ~cjwatson/launchpad:stormify-mailinglist into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:stormify-mailinglist into launchpad:master.
Commit message:
Convert MailingList and friends to Storm
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/387206
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-mailinglist into launchpad:master.
diff --git a/lib/lp/registry/model/mailinglist.py b/lib/lp/registry/model/mailinglist.py
index f7b7183..86619a0 100644
--- a/lib/lp/registry/model/mailinglist.py
+++ b/lib/lp/registry/model/mailinglist.py
@@ -19,16 +19,18 @@ from socket import getfqdn
from string import Template
from lazr.lifecycle.event import ObjectCreatedEvent
-from sqlobject import (
- ForeignKey,
- StringCol,
- )
-from storm.expr import (
+import pytz
+import six
+from storm.expr import Func
+from storm.locals import (
And,
- Func,
+ DateTime,
+ Int,
Join,
Or,
+ Reference,
Select,
+ Unicode,
)
from storm.info import ClassAlias
from storm.store import Store
@@ -64,17 +66,13 @@ from lp.services.database.constants import (
DEFAULT,
UTC_NOW,
)
-from lp.services.database.datetimecol import UtcDateTimeCol
from lp.services.database.decoratedresultset import DecoratedResultSet
-from lp.services.database.enumcol import EnumCol
+from lp.services.database.enumcol import DBEnum
from lp.services.database.interfaces import (
IMasterStore,
IStore,
)
-from lp.services.database.sqlbase import (
- SQLBase,
- sqlvalues,
- )
+from lp.services.database.stormbase import StormBase
from lp.services.database.stormexpr import Concatenate
from lp.services.identity.interfaces.account import AccountStatus
from lp.services.identity.interfaces.emailaddress import (
@@ -105,38 +103,46 @@ USABLE_STATUSES = (
@implementer(IMessageApproval)
-class MessageApproval(SQLBase):
+class MessageApproval(StormBase):
"""A held message."""
- message = ForeignKey(
- dbName='message', foreignKey='Message',
- notNull=True)
+ __storm_table__ = 'MessageApproval'
+
+ id = Int(primary=True)
+
+ _message_id = Int(name='message', allow_none=False)
+ message = Reference(_message_id, 'Message.id')
- posted_by = ForeignKey(
- dbName='posted_by', foreignKey='Person',
- storm_validator=validate_public_person,
- notNull=True)
+ posted_by_id = Int(
+ name='posted_by', validator=validate_public_person, allow_none=False)
+ posted_by = Reference(posted_by_id, 'Person.id')
- posted_message = ForeignKey(
- dbName='posted_message', foreignKey='LibraryFileAlias',
- notNull=True)
+ posted_message_id = Int(name='posted_message', allow_none=False)
+ posted_message = Reference(posted_message_id, 'LibraryFileAlias.id')
- posted_date = UtcDateTimeCol(notNull=True, default=UTC_NOW)
+ posted_date = DateTime(tzinfo=pytz.UTC, allow_none=False, default=UTC_NOW)
- mailing_list = ForeignKey(
- dbName='mailing_list', foreignKey='MailingList',
- notNull=True)
+ mailing_list_id = Int(name='mailing_list', allow_none=False)
+ mailing_list = Reference(mailing_list_id, 'MailingList.id')
- status = EnumCol(enum=PostedMessageStatus,
- default=PostedMessageStatus.NEW,
- notNull=True)
+ status = DBEnum(
+ enum=PostedMessageStatus, default=PostedMessageStatus.NEW,
+ allow_none=False)
- disposed_by = ForeignKey(
- dbName='disposed_by', foreignKey='Person',
- storm_validator=validate_public_person,
- default=None)
+ disposed_by_id = Int(
+ name='disposed_by', validator=validate_public_person, default=None)
+ disposed_by = Reference(disposed_by_id, 'Person.id')
- disposal_date = UtcDateTimeCol(default=None)
+ disposal_date = DateTime(tzinfo=pytz.UTC, default=None)
+
+ def __init__(self, message, posted_by, posted_message, posted_date,
+ mailing_list):
+ super(MessageApproval, self).__init__()
+ self.message = message
+ self.posted_by = posted_by
+ self.posted_message = posted_message
+ self.posted_date = posted_date
+ self.mailing_list = mailing_list
@property
def message_id(self):
@@ -175,7 +181,7 @@ class MessageApproval(SQLBase):
@implementer(IMailingList)
-class MailingList(SQLBase):
+class MailingList(StormBase):
"""The mailing list for a team.
Teams may have at most one mailing list, and a mailing list is associated
@@ -185,31 +191,39 @@ class MailingList(SQLBase):
XMLRPC).
"""
- team = ForeignKey(
- dbName='team', foreignKey='Person',
- notNull=True)
+ __storm_table__ = 'MailingList'
+
+ id = Int(primary=True)
+
+ team_id = Int(name='team', allow_none=False)
+ team = Reference(team_id, 'Person.id')
+
+ registrant_id = Int(
+ name='registrant', validator=validate_public_person, allow_none=False)
+ registrant = Reference(registrant_id, 'Person.id')
- registrant = ForeignKey(
- dbName='registrant', foreignKey='Person',
- storm_validator=validate_public_person, notNull=True)
+ date_registered = DateTime(
+ tzinfo=pytz.UTC, allow_none=False, default=DEFAULT)
- date_registered = UtcDateTimeCol(notNull=True, default=DEFAULT)
+ reviewer_id = Int(
+ name='reviewer', validator=validate_public_person, default=None)
+ reviewer = Reference(reviewer_id, 'Person.id')
- reviewer = ForeignKey(
- dbName='reviewer', foreignKey='Person',
- storm_validator=validate_public_person, default=None)
+ date_reviewed = DateTime(tzinfo=pytz.UTC, allow_none=True, default=None)
- date_reviewed = UtcDateTimeCol(notNull=False, default=None)
+ date_activated = DateTime(tzinfo=pytz.UTC, allow_none=True, default=None)
- date_activated = UtcDateTimeCol(notNull=False, default=None)
+ status = DBEnum(
+ enum=MailingListStatus, default=MailingListStatus.APPROVED,
+ allow_none=False)
- status = EnumCol(enum=MailingListStatus,
- default=MailingListStatus.APPROVED,
- notNull=True)
+ _welcome_message = Unicode(default=None, name='welcome_message')
- # Use a trailing underscore because SQLObject/importpedant doesn't like
- # the typical leading underscore.
- welcome_message_ = StringCol(default=None, dbName='welcome_message')
+ def __init__(self, team, registrant, date_registered=DEFAULT):
+ super(MailingList, self).__init__()
+ self.team = team
+ self.registrant = registrant
+ self.date_registered = date_registered
@property
def address(self):
@@ -295,7 +309,7 @@ class MailingList(SQLBase):
# than as a response to a user action.
removeSecurityProxy(email).status = (
EmailAddressStatus.VALIDATED)
- assert email.personID == self.teamID, (
+ assert email.personID == self.team_id, (
"Email already associated with another team.")
def _setAndNotifyDateActivated(self):
@@ -323,7 +337,7 @@ class MailingList(SQLBase):
if email is not None and self.team.preferredemail is not None:
if email.id == self.team.preferredemail.id:
self.team.setContactAddress(None)
- assert email.personID == self.teamID, 'Incorrectly linked email.'
+ assert email.personID == self.team_id, 'Incorrectly linked email.'
# Anyone with permission to deactivate a list can also set the
# email address status to NEW.
removeSecurityProxy(email).status = EmailAddressStatus.NEW
@@ -344,10 +358,12 @@ class MailingList(SQLBase):
"""See `IMailingList`."""
return self.status in USABLE_STATUSES
- def _get_welcome_message(self):
- return self.welcome_message_
+ @property
+ def welcome_message(self):
+ return self._welcome_message
- def _set_welcome_message(self, text):
+ @welcome_message.setter
+ def welcome_message(self, text):
if self.status == MailingListStatus.REGISTERED:
# Do nothing because the status does not change. When setting the
# welcome_message on a newly registered mailing list the XMLRPC
@@ -362,14 +378,12 @@ class MailingList(SQLBase):
self.status = MailingListStatus.MODIFIED
else:
raise AssertionError('Only usable mailing lists may be modified')
- self.welcome_message_ = text
-
- welcome_message = property(_get_welcome_message, _set_welcome_message)
+ self._welcome_message = text
def getSubscription(self, person):
"""See `IMailingList`."""
- return MailingListSubscription.selectOneBy(person=person,
- mailing_list=self)
+ return Store.of(self).find(
+ MailingListSubscription, person=person, mailing_list=self).one()
def getSubscribers(self):
"""See `IMailingList`."""
@@ -409,7 +423,7 @@ class MailingList(SQLBase):
raise CannotUnsubscribe(
'%s is not a member of the mailing list: %s' %
(person.displayname, self.team.displayname))
- subscription.destroySelf()
+ Store.of(subscription).remove(subscription)
def changeAddress(self, person, address):
"""See `IMailingList`."""
@@ -422,10 +436,7 @@ class MailingList(SQLBase):
raise CannotChangeSubscription(
'%s does not own the email address: %s' %
(person.displayname, address.email))
- if address is None:
- subscription.email_address = None
- else:
- subscription.email_addressID = address.id
+ subscription.email_address = address
def holdMessage(self, message):
"""See `IMailingList`."""
@@ -441,10 +452,10 @@ class MailingList(SQLBase):
"""See `IMailingList`."""
store = Store.of(self)
clauses = [
- MessageApproval.mailing_listID == self.id,
+ MessageApproval.mailing_list == self,
MessageApproval.status == PostedMessageStatus.NEW,
- MessageApproval.messageID == Message.id,
- MessageApproval.posted_byID == Person.id
+ MessageApproval.message == Message.id,
+ MessageApproval.posted_by == Person.id
]
if message_id_filter is not None:
clauses.append(Message.rfc822msgid.is_in(message_id_filter))
@@ -533,28 +544,27 @@ class MailingListSet:
def get(self, team_name):
"""See `IMailingListSet`."""
- assert isinstance(team_name, basestring), (
- 'team_name must be a string, not %s' % type(team_name))
- return MailingList.selectOne("""
- MailingList.team = Person.id
- AND Person.name = %s
- AND Person.teamowner IS NOT NULL
- """ % sqlvalues(team_name),
- clauseTables=['Person'])
+ assert isinstance(team_name, six.text_type), (
+ 'team_name must be a text string, not %s' % type(team_name))
+ return IStore(MailingList).find(
+ MailingList,
+ MailingList.team == Person.id,
+ Person.name == team_name,
+ Person.teamowner != None).one()
def getSubscriptionsForTeams(self, person, teams):
"""See `IMailingListSet`."""
store = IStore(MailingList)
team_ids = set(map(operator.attrgetter("id"), teams))
lists = dict(store.find(
- (MailingList.teamID, MailingList.id),
- MailingList.teamID.is_in(team_ids),
+ (MailingList.team_id, MailingList.id),
+ MailingList.team_id.is_in(team_ids),
MailingList.status.is_in(USABLE_STATUSES)))
subscriptions = dict(store.find(
- (MailingListSubscription.mailing_listID,
+ (MailingListSubscription.mailing_list_id,
MailingListSubscription.id),
MailingListSubscription.person == person,
- MailingListSubscription.mailing_listID.is_in(lists.values())))
+ MailingListSubscription.mailing_list_id.is_in(lists.values())))
by_team = {}
for team, mailing_list in lists.items():
by_team[team] = (mailing_list, subscriptions.get(mailing_list))
@@ -585,25 +595,25 @@ class MailingListSet:
Join(TeamParticipation, TeamParticipation.personID == Person.id),
Join(
MailingListSubscription,
- MailingListSubscription.personID == Person.id),
+ MailingListSubscription.person_id == Person.id),
Join(
MailingList,
- MailingList.id == MailingListSubscription.mailing_listID),
- Join(Team, Team.id == MailingList.teamID),
+ MailingList.id == MailingListSubscription.mailing_list_id),
+ Join(Team, Team.id == MailingList.team_id),
)
team_ids, list_ids = self._getTeamIdsAndMailingListIds(team_names)
preferred = store.using(*tables).find(
(EmailAddress.email, Person.display_name, Team.name),
- And(MailingListSubscription.mailing_listID.is_in(list_ids),
+ And(MailingListSubscription.mailing_list_id.is_in(list_ids),
TeamParticipation.teamID.is_in(team_ids),
- MailingList.teamID == TeamParticipation.teamID,
+ MailingList.team_id == TeamParticipation.teamID,
MailingList.status != MailingListStatus.INACTIVE,
Account.status == AccountStatus.ACTIVE,
Or(
- And(MailingListSubscription.email_addressID == None,
+ And(MailingListSubscription.email_address_id == None,
EmailAddress.status == EmailAddressStatus.PREFERRED),
EmailAddress.id ==
- MailingListSubscription.email_addressID),
+ MailingListSubscription.email_address_id),
))
# Sort by team name.
by_team = collections.defaultdict(set)
@@ -631,8 +641,8 @@ class MailingListSet:
Join(Account, Account.id == Person.accountID),
Join(EmailAddress, EmailAddress.personID == Person.id),
Join(TeamParticipation, TeamParticipation.personID == Person.id),
- Join(MailingList, MailingList.teamID == TeamParticipation.teamID),
- Join(Team, Team.id == MailingList.teamID),
+ Join(MailingList, MailingList.team_id == TeamParticipation.teamID),
+ Join(Team, Team.id == MailingList.team_id),
)
team_ids, list_ids = self._getTeamIdsAndMailingListIds(team_names)
team_members = store.using(*tables).find(
@@ -652,14 +662,14 @@ class MailingListSet:
Person,
Join(Account, Account.id == Person.accountID),
Join(EmailAddress, EmailAddress.personID == Person.id),
- Join(MessageApproval, MessageApproval.posted_byID == Person.id),
+ Join(MessageApproval, MessageApproval.posted_by_id == Person.id),
Join(MailingList,
- MailingList.id == MessageApproval.mailing_listID),
- Join(Team, Team.id == MailingList.teamID),
+ MailingList.id == MessageApproval.mailing_list_id),
+ Join(Team, Team.id == MailingList.team_id),
)
approved_posters = store.using(*tables).find(
(Team.name, Person.display_name, EmailAddress.email),
- And(MessageApproval.mailing_listID.is_in(list_ids),
+ And(MessageApproval.mailing_list_id.is_in(list_ids),
MessageApproval.status.is_in(MESSAGE_APPROVAL_STATUSES),
EmailAddress.status.is_in(EMAIL_ADDRESS_STATUSES),
Account.status == AccountStatus.ACTIVE,
@@ -681,28 +691,34 @@ class MailingListSet:
@property
def approved_lists(self):
"""See `IMailingListSet`."""
- return MailingList.selectBy(status=MailingListStatus.APPROVED)
+ return IStore(MailingList).find(
+ MailingList, status=MailingListStatus.APPROVED)
@property
def active_lists(self):
"""See `IMailingListSet`."""
- return MailingList.selectBy(status=MailingListStatus.ACTIVE)
+ return IStore(MailingList).find(
+ MailingList, status=MailingListStatus.ACTIVE)
@property
def modified_lists(self):
"""See `IMailingListSet`."""
- return MailingList.selectBy(status=MailingListStatus.MODIFIED)
+ return IStore(MailingList).find(
+ MailingList, status=MailingListStatus.MODIFIED)
@property
def deactivated_lists(self):
"""See `IMailingListSet`."""
- return MailingList.selectBy(status=MailingListStatus.DEACTIVATING)
+ return IStore(MailingList).find(
+ MailingList, status=MailingListStatus.DEACTIVATING)
@property
def unsynchronized_lists(self):
"""See `IMailingListSet`."""
- return MailingList.select('status IN %s' % sqlvalues(
- (MailingListStatus.CONSTRUCTING, MailingListStatus.UPDATING)))
+ return IStore(MailingList).find(
+ MailingList,
+ MailingList.status.is_in(
+ (MailingListStatus.CONSTRUCTING, MailingListStatus.UPDATING)))
def updateTeamAddresses(self, old_hostname):
"""See `IMailingListSet`."""
@@ -718,7 +734,7 @@ class MailingListSet:
clauses = [
EmailAddress.person == Person.id,
Person.teamowner != None,
- Person.id == MailingList.teamID,
+ Person.id == MailingList.team_id,
EmailAddress.email.endswith(old_suffix),
]
addresses = IMasterStore(EmailAddress).find(
@@ -730,22 +746,30 @@ class MailingListSet:
@implementer(IMailingListSubscription)
-class MailingListSubscription(SQLBase):
+class MailingListSubscription(StormBase):
"""A mailing list subscription."""
- person = ForeignKey(
- dbName='person', foreignKey='Person',
- storm_validator=validate_public_person,
- notNull=True)
+ __storm_table__ = 'MailingListSubscription'
- mailing_list = ForeignKey(
- dbName='mailing_list', foreignKey='MailingList',
- notNull=True)
+ id = Int(primary=True)
- date_joined = UtcDateTimeCol(notNull=True, default=UTC_NOW)
+ person_id = Int(
+ name='person', validator=validate_public_person, allow_none=False)
+ person = Reference(person_id, 'Person.id')
- email_address = ForeignKey(dbName='email_address',
- foreignKey='EmailAddress')
+ mailing_list_id = Int(name='mailing_list', allow_none=False)
+ mailing_list = Reference(mailing_list_id, 'MailingList.id')
+
+ date_joined = DateTime(tzinfo=pytz.UTC, allow_none=False, default=UTC_NOW)
+
+ email_address_id = Int(name='email_address')
+ email_address = Reference(email_address_id, 'EmailAddress.id')
+
+ def __init__(self, person, mailing_list, email_address):
+ super(MailingListSubscription, self).__init__()
+ self.person = person
+ self.mailing_list = mailing_list
+ self.email_address = email_address
@property
def subscribed_address(self):
@@ -764,11 +788,10 @@ class MessageApprovalSet:
def getMessageByMessageID(self, message_id):
"""See `IMessageApprovalSet`."""
- return MessageApproval.selectOne("""
- MessageApproval.message = Message.id AND
- Message.rfc822msgid = %s
- """ % sqlvalues(message_id),
- distinct=True, clauseTables=['Message'])
+ return IStore(MessageApproval).find(
+ MessageApproval,
+ MessageApproval.message == Message.id,
+ Message.rfc822msgid == message_id).config(distinct=True).one()
def getHeldMessagesWithStatus(self, status):
"""See `IMessageApprovalSet`."""
diff --git a/lib/lp/registry/scripts/mlistimport.py b/lib/lp/registry/scripts/mlistimport.py
index 368cc9a..f8d5951 100644
--- a/lib/lp/registry/scripts/mlistimport.py
+++ b/lib/lp/registry/scripts/mlistimport.py
@@ -11,6 +11,7 @@ __all__ = [
from email.utils import parseaddr
+import six
from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
@@ -32,15 +33,15 @@ class Importer:
"""Perform mailing list imports for command line scripts."""
def __init__(self, team_name, log=None):
- self.team_name = team_name
- self.team = getUtility(IPersonSet).getByName(team_name)
+ self.team_name = six.ensure_text(team_name)
+ self.team = getUtility(IPersonSet).getByName(self.team_name)
assert self.team is not None, (
- 'No team with name: %s' % team_name)
- self.mailing_list = getUtility(IMailingListSet).get(team_name)
+ 'No team with name: %s' % self.team_name)
+ self.mailing_list = getUtility(IMailingListSet).get(self.team_name)
assert self.mailing_list is not None, (
- 'Team has no mailing list: %s' % team_name)
+ 'Team has no mailing list: %s' % self.team_name)
assert self.mailing_list.status == MailingListStatus.ACTIVE, (
- 'Team mailing list is not active: %s' % team_name)
+ 'Team mailing list is not active: %s' % self.team_name)
if log is None:
self.log = BufferLogger()
else:
diff --git a/lib/lp/registry/stories/mailinglists/lifecycle.txt b/lib/lp/registry/stories/mailinglists/lifecycle.txt
index 5a37931..d18630a 100644
--- a/lib/lp/registry/stories/mailinglists/lifecycle.txt
+++ b/lib/lp/registry/stories/mailinglists/lifecycle.txt
@@ -284,7 +284,7 @@ to delete the archives of an INACTIVE list, this must be done manually.
>>> from zope.component import getUtility
>>> from lp.registry.interfaces.mailinglist import IMailingListSet
- >>> def print_list_state(team_name='aardvarks'):
+ >>> def print_list_state(team_name=u'aardvarks'):
... login('foo.bar@xxxxxxxxxxxxx')
... mailing_list = getUtility(IMailingListSet).get(team_name)
... print mailing_list.status.name
@@ -358,7 +358,7 @@ be purged.
>>> def show_states(*states):
... url = 'http://launchpad.test/~aardvarks/+mailinglist'
... for status in states:
- ... set_list_state('aardvarks', status)
+ ... set_list_state(u'aardvarks', status)
... print_list_state()
... admin_browser.open(url)
... print purge_text(admin_browser)
@@ -367,7 +367,7 @@ be purged.
A purged list acts as if it doesn't even exist.
- >>> set_list_state('aardvarks', MailingListStatus.PURGED)
+ >>> set_list_state(u'aardvarks', MailingListStatus.PURGED)
>>> print_list_state()
PURGED
>>> admin_browser.open('http://launchpad.test/~aardvarks/+mailinglist')
@@ -379,4 +379,4 @@ A purged list acts as if it doesn't even exist.
The team owner can see that an inactive list can be reactivated or purged.
- >>> set_list_state('aardvarks', MailingListStatus.INACTIVE)
+ >>> set_list_state(u'aardvarks', MailingListStatus.INACTIVE)
diff --git a/lib/lp/registry/stories/mailinglists/welcome-message.txt b/lib/lp/registry/stories/mailinglists/welcome-message.txt
index d419457..2565616 100644
--- a/lib/lp/registry/stories/mailinglists/welcome-message.txt
+++ b/lib/lp/registry/stories/mailinglists/welcome-message.txt
@@ -14,9 +14,8 @@ address.
>>> user_browser.open('http://launchpad.test/~aardvarks')
>>> user_browser.getLink('Configure mailing list').click()
>>> welcome_message = user_browser.getControl('Welcome message')
- >>> welcome_message.value
- ''
- >>> welcome_message.value = 'Welcome to the Aardvarks.'
+ >>> print(welcome_message.value)
+ >>> welcome_message.value = u'Welcome to the Aardvarks.'
>>> user_browser.getControl('Save').click()
Changes to the welcome message take effect as soon as Mailman can act on it.
@@ -34,14 +33,14 @@ Changes to the welcome message take effect as soon as Mailman can act on it.
What if Mailman failed to apply the change?
- >>> welcome_message.value = 'This change will fail to propagate.'
+ >>> welcome_message.value = u'This change will fail to propagate.'
>>> user_browser.getControl('Save').click()
# Re-fetch the mailing list, this time from the utility.
>>> from lp.registry.interfaces.mailinglist import IMailingListSet
>>> login('foo.bar@xxxxxxxxxxxxx')
>>> from zope.component import getUtility
- >>> mailing_list = getUtility(IMailingListSet).get('aardvarks')
+ >>> mailing_list = getUtility(IMailingListSet).get(u'aardvarks')
>>> mailing_list.status
<DBItem MailingListStatus.MODIFIED, (8) Modified>
diff --git a/lib/lp/registry/tests/test_mailinglist.py b/lib/lp/registry/tests/test_mailinglist.py
index 641b6b4..7b58a0a 100644
--- a/lib/lp/registry/tests/test_mailinglist.py
+++ b/lib/lp/registry/tests/test_mailinglist.py
@@ -1,6 +1,8 @@
# Copyright 2009 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
+from __future__ import absolute_import, print_function, unicode_literals
+
__metaclass__ = type
__all__ = []
diff --git a/lib/lp/registry/tests/test_mailinglistapi.py b/lib/lp/registry/tests/test_mailinglistapi.py
index e967e3e..3503db2 100644
--- a/lib/lp/registry/tests/test_mailinglistapi.py
+++ b/lib/lp/registry/tests/test_mailinglistapi.py
@@ -3,6 +3,8 @@
"""Unit tests for the private MailingList API."""
+from __future__ import absolute_import, print_function, unicode_literals
+
__metaclass__ = type
__all__ = []
@@ -494,7 +496,7 @@ class MailingListAPIMessageTestCase(TestCaseWithFactory):
Message-ID: <\xa9-me>
Date: Fri, 01 Aug 2000 01:08:59 -0000\n
I put \xa9 in the body.
- """))
+ """).encode('ISO-8859-1'))
info = self.mailinglist_api.holdMessage(
'team', xmlrpc_client.Binary(message.as_string()))
transaction.commit()
@@ -507,13 +509,13 @@ class MailingListAPIMessageTestCase(TestCaseWithFactory):
finally:
found.posted_message.close()
self.assertEqual([
- 'From: \\xa9 me <me@xxxxxx>',
- 'To: team@xxxxxxxxxxxxxxxxxxxx',
- 'Subject: \\xa9 gremlins',
- 'Message-ID: <\\xa9-me>',
- 'Date: Fri, 01 Aug 2000 01:08:59 -0000',
- '',
- 'I put \xa9 in the body.'], text.splitlines())
+ b'From: \\xa9 me <me@xxxxxx>',
+ b'To: team@xxxxxxxxxxxxxxxxxxxx',
+ b'Subject: \\xa9 gremlins',
+ b'Message-ID: <\\xa9-me>',
+ b'Date: Fri, 01 Aug 2000 01:08:59 -0000',
+ b'',
+ b'I put \xa9 in the body.'], text.splitlines())
def test_getMessageDispositions_accept(self):
# List moderators can approve messages.
diff --git a/lib/lp/registry/tests/test_mlists.py b/lib/lp/registry/tests/test_mlists.py
index 5125753..f7325c8 100644
--- a/lib/lp/registry/tests/test_mlists.py
+++ b/lib/lp/registry/tests/test_mlists.py
@@ -3,6 +3,8 @@
"""Test mailing list stuff."""
+from __future__ import absolute_import, print_function, unicode_literals
+
__metaclass__ = type
@@ -16,6 +18,7 @@ from subprocess import (
import tempfile
import unittest
+import six
import transaction
from zope.component import getUtility
@@ -81,12 +84,9 @@ class BaseMailingListImportTest(unittest.TestCase):
def writeFile(self, *addresses):
# Write the addresses to import to our open temporary file.
- out_file = open(self.filename, 'w')
- try:
+ with open(self.filename, 'w') as out_file:
for address in addresses:
- print >> out_file, address
- finally:
- out_file.close()
+ print(address, file=out_file)
def assertPeople(self, *people):
"""Assert that `people` are members of the team."""
@@ -364,7 +364,7 @@ class TestMailingListImports(BaseMailingListImportTest):
def test_import_existing_with_nonascii_name(self):
# Make sure that a person with a non-ascii name, who's already a
# member of the list, gets a proper log message.
- self.anne.display_name = u'\u1ea2nn\u1ebf P\u1ec5rs\u1ed1n'
+ self.anne.display_name = '\u1ea2nn\u1ebf P\u1ec5rs\u1ed1n'
importer = Importer('aardvarks', self.logger)
self.anne.join(self.team)
self.mailing_list.subscribe(self.anne)
@@ -373,9 +373,8 @@ class TestMailingListImports(BaseMailingListImportTest):
'bperson@xxxxxxxxxxx',
))
self.assertEqual(
- self.logger.getLogBuffer(),
- 'ERROR \xe1\xba\xa2nn\xe1\xba\xbf '
- 'P\xe1\xbb\x85rs\xe1\xbb\x91n is already subscribed '
+ six.ensure_text(self.logger.getLogBuffer()),
+ 'ERROR \u1ea2nn\u1ebf P\u1ec5rs\u1ed1n is already subscribed '
'to list Aardvarks\n'
'INFO anne.person@xxxxxxxxxxx (anne) joined and subscribed\n'
'INFO bperson@xxxxxxxxxxx (bart) joined and subscribed\n')
diff --git a/lib/lp/registry/vocabularies.py b/lib/lp/registry/vocabularies.py
index 80fb0fd..bd259ea 100644
--- a/lib/lp/registry/vocabularies.py
+++ b/lib/lp/registry/vocabularies.py
@@ -179,7 +179,10 @@ from lp.services.database.sqlbase import (
SQLBase,
sqlvalues,
)
-from lp.services.database.stormexpr import RegexpMatch
+from lp.services.database.stormexpr import (
+ fti_search,
+ RegexpMatch,
+ )
from lp.services.helpers import (
ensure_unicode,
shortlist,
@@ -1039,13 +1042,11 @@ class ActiveMailingListVocabulary(FilteredVocabularyBase):
return getUtility(IMailingListSet).active_lists
# The mailing list name, such as it has one, is really the name of the
# team to which it is linked.
- return MailingList.select("""
- MailingList.team = Person.id
- AND Person.fti @@ ftq(%s)
- AND Person.teamowner IS NOT NULL
- AND MailingList.status = %s
- """ % sqlvalues(text, MailingListStatus.ACTIVE),
- clauseTables=['Person'])
+ return IStore(MailingList).find(
+ MailingList.team == Person.id,
+ fti_search(Person, text),
+ Person.teamowner != None,
+ MailingList.status == MailingListStatus.ACTIVE)
def searchForTerms(self, query=None, vocab_filter=None):
"""See `IHugeVocabulary`."""
diff --git a/lib/lp/services/identity/model/emailaddress.py b/lib/lp/services/identity/model/emailaddress.py
index 5251928..00697f9 100644
--- a/lib/lp/services/identity/model/emailaddress.py
+++ b/lib/lp/services/identity/model/emailaddress.py
@@ -21,6 +21,7 @@ from zope.interface import implementer
from lp.app.validators.email import valid_email
from lp.services.database.enumcol import EnumCol
+from lp.services.database.interfaces import IMasterStore
from lp.services.database.sqlbase import (
quote,
SQLBase,
@@ -80,9 +81,10 @@ class EmailAddress(SQLBase, HasOwnerMixin):
# XXX 2009-05-04 jamesh bug=371567: This function should not
# be responsible for removing subscriptions, since the SSO
# server can't write to that table.
- for subscription in MailingListSubscription.selectBy(
- email_address=self):
- subscription.destroySelf()
+ store = IMasterStore(MailingListSubscription)
+ for subscription in store.find(
+ MailingListSubscription, email_address=self):
+ store.remove(subscription)
super(EmailAddress, self).destroySelf()
@property