5

I have an email that I'm reading with the Python email lib that I need to modify the attachments of. The email Message class has the "attach" method, but does not have anything like "detach". How can I remove an attachment from a multipart message? If possible, I want to do this without recreating the message from scratch.

Essentially I want to:

  1. Load the email
  2. Remove the mime attachments
  3. Add a new attachment
john a macdonald
  • 975
  • 1
  • 7
  • 9
  • https://stackoverflow.com/questions/69850757/python-eml-file-edit is basically the same question for Python 3. I did not want to close it as a duplicate, as the answers here are either specific to Python 2 or vague. – tripleee Nov 05 '21 at 10:21

4 Answers4

3

Well, from my experience, in the context you are working, everything is a Message object. The message, its parts, attachments, everything. So, to accomplish what you want to do, you need to

  1. parse the message using the Parser API (this will get you the root Message object)
  2. Walk the structure, determining what you need and what you don't (using a method of a Message instance, - .walk()), - remember, that everything is a Message.
  3. Attach whatever you need to attach to the parts you've extracted and you are good to go.

To reiterate, what you are working with is, essentially, a tree, where Message objects with .is_multipart() == True are nodes and Message objects with .is_multipart() == False are end-nodes (their payload is a string, not a bunch of Message objects).

shylent
  • 10,076
  • 6
  • 38
  • 55
3

The way I've figured out to do it is:

  1. Set the payload to an empty list with set_payload
  2. Create the payload, and attach to the message.
john a macdonald
  • 975
  • 1
  • 7
  • 9
3

set_payload() may help.

set_payload(payload[, charset])

Set the entire message object’s payload to payload. It is the client’s responsibility to ensure the payload invariants.

A quick interactive example:

>>> from email import mime,message
>>> m1 = message.Message()
>>> t1=email.MIMEText.MIMEText('t1\r\n')
>>> print t1.as_string()
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

t1

>>> m1.attach(t1)
>>> m1.is_multipart()
True
>>> m1.get_payload()
[<email.mime.text.MIMEText instance at 0x00F585A8>]
>>> t2=email.MIMEText.MIMEText('t2\r\n')
>>> m1.set_payload([t2])
>>> print m1.get_payload()[0].as_string()
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

t2

>>> 
Community
  • 1
  • 1
gimel
  • 83,368
  • 10
  • 76
  • 104
  • Notice that the `email` API has been updated considerably in Python 3.6; you do not want to use the old API in new code any longer. – tripleee Nov 05 '21 at 10:21
0

For MIMEMultipart you can attach, but you can't remove elements directly. Good news is that in this case get_payload() returns a standard python list of elements.

From https://docs.python.org/3/library/email.compat32-message.html

Return the current payload, which will be a list of Message objects when is_multipart() is True, or a string when is_multipart() is False. If the payload is a list and you mutate the list object, you modify the message’s payload in place.

To clear entire MIMEMultipart payload just assign new empty list:

multipart_related.set_payload([])

This example removes only selected items from multipart/related payload list:

  for part in email.walk():
    if part.get_content_type() == 'multipart/related':
      list_of_messages = part.get_payload()
      for message in list(list_of_messages):
        if message.get_content_type() != 'text/html' and message.get_content_type() != 'text/plain':
          list_of_messages.remove(part)
mzmrk
  • 109
  • 1
  • 4