[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