From f321d85d91a370294e771dbaa22493008d78dfdd Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 31 Oct 2007 17:38:51 -0400 Subject: Much progress, though not perfect, on migrating to SQLAlchemy 0.4 and Elixir 0.4. Lots of things changes, which broke lots of our code. There are still a couple of failures in the test suite that I don't understand. It seems that for pending.txt and requests.txt, sometimes strings come back from the database as 8-bit strings and other times as unicodes. It's impossible to make these tests work both separately and together. users.txt is also failing intermittently. Lots of different behavior between running the full test suite all together and running individual tests. Sigh. Note also that actually, Elixir 0.4.0 doesn't work for us. There's a bug in that version that prevented zope.interfaces and Elixir working together. Get the latest 0.4.0 from source to fix this. Other changes include: - Remove Mailman/lockfile.py. While I haven't totally eliminated locking, I have released the lockfile as a separate Python package called locknix, which Mailman 3.0 now depends on. - Renamed Mailman/interfaces/messagestore.py and added an IMessage interface. - bin/testall raises turns on SQLALCHEMY_ECHO when the verbosity is above 3 (that's three -v's because the default verbosity is 1). - add_domain() in config files now allows url_host to be optional. If not given, it defaults to email_host. - Added a non-public interface IDatabase._reset() used by the test suite to zap the database between doctests. Added an implementation in the model which just runs through all rows in all entities, deleting them. - [I]Pending renamed to [I]Pended - Don't allow Pendings.add() to infloop. - In the model's User impelementations, we don't need to append or remove the address when linking and unlinking. By setting the address.user attribute, SQLAlchemy appears to do the right thing, though I'm not 100% sure of that (see the above mentioned failures). --- Mailman/interfaces/messages.py | 112 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Mailman/interfaces/messages.py (limited to 'Mailman/interfaces/messages.py') diff --git a/Mailman/interfaces/messages.py b/Mailman/interfaces/messages.py new file mode 100644 index 000000000..9fac98d76 --- /dev/null +++ b/Mailman/interfaces/messages.py @@ -0,0 +1,112 @@ +# Copyright (C) 2007 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. + +"""The message storage service.""" + +from zope.interface import Interface, Attribute + + + +class IMessageStore(Interface): + """The interface of the global message storage service. + + All messages that are stored in the system live in the message storage + service. This store is responsible for providing unique identifiers for + every message stored in it. A message stored in this service must have at + least a Message-ID header and a Date header. These are not guaranteed to + be unique, so the service also provides a unique sequence number to every + message. + + Storing a message returns the unique sequence number for the message. + This sequence number will be stored on the message's + X-List-Sequence-Number header. Any previous such header value will be + overwritten. An X-List-ID-Hash header will also be added, containing the + Base-32 encoded SHA1 hash of the message's Message-ID and Date headers. + + The combination of the X-List-ID-Hash header and the + X-List-Sequence-Number header uniquely identify this message to the + storage service. A globally unique URL that addresses this message may be + crafted from these headers and the List-Archive header as follows. For a + message with the following headers: + + Message-ID: <87myycy5eh.fsf@uwakimon.sk.tsukuba.ac.jp> + Date: Wed, 04 Jul 2007 16:49:58 +0900 + List-Archive: http://archive.example.com/ + X-List-ID-Hash: RXTJ357KFOTJP3NFJA6KMO65X7VQOHJI + X-List-Sequence-Number: 801 + + the globally unique URL would be: + + http://archive.example.com/RXTJ357KFOTJP3NFJA6KMO65X7VQOHJI/801 + """ + + def add(message): + """Add the message to the store. + + :param message: An email.message.Message instance containing at least + a Message-ID header and a Date header. The message will be given + an X-List-ID-Hash header and an X-List-Sequence-Number header. + :returns: The message's sequence ID as an integer. + :raises ValueError: if the message is missing one of the required + headers. + """ + + def get_messages_by_message_id(message_id): + """Return the set of messages with the matching Message-ID. + + :param message_id: The Message-ID header contents to search for. + :returns: An iterator over all the matching messages. + """ + + def get_messages_by_hash(hash): + """Return the set of messages with the matching X-List-ID-Hash. + + :param hash: The X-List-ID-Hash header contents to search for. + :returns: An iterator over all the matching messages. + """ + + def get_message(global_id): + """Return the message with the matching hash and sequence number. + + :param global_id: The global relative ID which uniquely addresses this + message, relative to the base address of the message store. This + must be a string of the X-List-ID-Hash followed by a single slash + character, followed by the X-List-Sequence-Number. + :returns: The matching message, or None if there is no match. + """ + + def delete_message(global_id): + """Remove the addressed message from the store. + + :param global_id: The global relative ID which uniquely addresses the + message to delete. + :raises KeyError: if there is no such message. + """ + + messages = Attribute( + """An iterator over all messages in this message store.""") + + + +class IMessage(Interface): + """The representation of an email message.""" + + hash = Attribute("""The unique SHA1 hash of the message.""") + + path = Attribute("""The filesystem path to the message object.""") + + message_id = Attribute("""The message's Message-ID header.""") -- cgit v1.2.3-70-g09d2