Title: EmailMessage.as_string is altering the message state and actually fix bugs
Type: behavior Stage:
Components: email Versions: Python 3.8, Python 3.7
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: barry, mardiros, r.david.murray
Priority: normal Keywords:

Created on 2020-06-28 09:43 by mardiros, last changed 2020-07-10 16:58 by r.david.murray.

File name Uploaded Description Edit mardiros, 2020-06-28 09:43
Messages (2)
msg372508 - (view) Author: Guillaume Gauvrit (mardiros) * Date: 2020-06-28 09:43
I am currently refactoring code and use the EmailMessage api
to build message.

I encountered weird behavior while building an email.
The `as_string()` method is fixing the `make_alternative` method.

So, to me their is two bug here: a `as_string` method should not mutate internal state,
and the `make_alternative` should create a correct internal state.

It may be resume in the following program:

𝝿 python
Python 3.8.3 (default, May 17 2020, 18:15:42)
[GCC 10.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import email
>>> from email.message import EmailMessage, MIMEPart
>>> msg = EmailMessage()
>>> msg.make_alternative()
>>> print(msg.get_boundary())
>>> print(msg._headers)
[('Content-Type', 'multipart/alternative')]
>>> _ = msg.as_string()
>>> print(msg.get_boundary())
>>> print(msg._headers)
[('Content-Type', 'multipart/alternative; boundary="===============3171625413581695247=="')]
msg373472 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2020-07-10 16:58
The as_strings docs say:

"Flattening the message may trigger changes to the Message if defaults need to be filled in to complete the transformation to a string (for example, MIME boundaries may be generated or modified)."

So, while this is indeed an API design bug, it isn't an actual bug in the code but rather is expected behavior, currently.  The historical reason for this is that the generator code looks at the entire message to make sure the boundary string is unique.  My long term plan for email included plans to rewrite the generator, and I was going to fix this issue at that point.  My life got too busy to be able to continue with email development work, though, so that never happened.

It has been *years* since I've looked at the code.  Thinking about it now, I'm wondering if it would be possible to use a GUID technique to generate the boundary and thus do exactly as you say: have make_alternative (and anything else that causes a boundary to be needed) pre-create the boundary.  That, I think, would mean we wouldn't need to change the generator, even though it would still be doing its (inefficient) check that the boundary was unique.  I'm not sure if it would work, though; it's been too long since I've looked at the relevant code.
Date User Action Args
2020-07-10 16:58:06r.david.murraysettype: resource usage -> behavior
messages: + msg373472
2020-07-10 08:41:23eric.smithsetnosy: + barry, r.david.murray
components: + email
2020-06-28 09:43:33mardiroscreate