[Mailman-Developers] Modifications to msg

Barry A. Warsaw barry@zope.com
Mon, 18 Mar 2002 19:39:32 -0500


>>>>> "ES" == Ellen Spertus <spertus@mills.edu> writes:

    ES> I've been making some modifications to Mailman to support
    ES> dynamic sublists (http://javamlm.mills.edu).  I'm still rather
    ES> new with Python and have a question about modifying fields
    ES> (dictionary elements) of the msg data structure.

It's important to remember that the Message class is not a dictionary,
even though it supports dictionary-like syntax.  Python is funny in
that there is no concrete interface specification, so we tend to say
something is "mapping-like" or "file-like" if it supports common
methods of those built-in types, but those similes don't really mean
anything.  But they are amorphous because "file-like" might mean the
object supports a write() method with the same signature as a file
object's write(), but what does that say about a "file-like" thing's
read() method, etc.?  Nothing, actually. :)

Mapping-like is even more information-free.  Usually a mapping-like
object supports __getitem__(), and keys(), but does it support
items(), values(), setdefault(), etc.?  Who knows? <wink>

When in doubt, we have to be concrete about what API a class provides
or expects, e.g. /this/ method, or /that/ signature.

    ES> Specifically, I added the following code to change the
    ES> message's "To" field at the end of CookHeaders.py:

    | 1    if msgdata.get('dlist'):
    | 2        threadID = msgdata['thread_id']
    | 3        syslog('info', "threadID = %d", threadID)
    | 4        to_line =  '%s-%d@%s' % (mlist.real_name.lower(),
    |                                    threadID,
    |                                    mlist.host_name)
    | 5        syslog('info', "to_line = %s", to_line)
    | 6        del msg['To']
    | 7        msg['To'] = to_line
    | 8        syslog('info', "Set msg['To'] to '%s'", msg['To'])

    ES> This correctly changes the "To" field of the message from
    ES> etest-new@hostname to etest-<threadID>@hostname, as shown by
    ES> the log: Mar 18 13:29:55 2002 (4600) threadID = 19 Mar 18
    ES> 13:29:55 2002 (4600) to_line = etest-19@javamlm.mills.edu Mar
    ES> 18 13:29:55 2002 (4600) Set msg['To'] to
    ES> 'etest-19@javamlm.mills.edu'

    ES> Similarly, the resulting email message that is sent has the
    ES> new 'To' field.

    ES> The problem is I don't see why line 6 above is necessary.

Here's where the Message class has purposefully different semantics
than a dictionary.  Specifically, because email messages can have more
than one of some kinds of headers, the Message class supports this as
well.  Therefore __setitem__() -- which is what "msg['to']" maps to --
does not delete existing To: headers.  However, I didn't want
__getitem__() -- aka msg['to'] -- to return anything but a string, so
I decided that in the face of multiple headers, __getitem__() would
return "one of them".  Which one it returns is purposefully left
undetermined.

That's why Message.get_all() exists, so that you can get all the To:
headers the message might have had.  That's also why you must call del
first -- aka __delitem__() -- if you want to overwrite all the To:
headers.

This is all spelled out in the module documentation:

http://www.python.org/doc/current/lib/module-email.Message.html

Aside: one of the reasons why I want this behavior is because I'd like
the Message class (or a derived class possibly) to eventually enforce
RFC 2822 rules on the number of specific headers a message can have.
E.g. it can have many Received: headers, but only one Reply-To: header
(although the latter allows for multiple addresses... go figure).

I hope that helps,
-Barry