python-3.xemailemail-headers

Python 3.x email.message library: trying to remove specific header content such as bcc


The library functions for reading headers from an RFC822 compliant file are working just fine for me, for example:

    allRecips = []
    for hdrName in ['to', 'cc', 'bcc']:
        for i in email.utils.getaddresses(msg.get_all(hdrName, [])):
            r = {
                'address': {
                    'name': i[0],
                    'email': i[1]
                }
            }
            allRecips.append(r)

I now want to remove the bcc recipients from the msg structure in the above example. I looked at del_param() for this, but couldn't figure out what to pass in. Is there a good way to delete any bcc headers that may be present (1 or more)?


Solution

  • What you described is exactly what Message.__delitem__() (which EmailMessage.__delitem__() inherits from, if you are using the new API) does:

    __delitem__(name)

    Delete all occurrences of the field with name name from the message’s headers. No exception is raised if the named field isn’t present in the headers.

    so you can simply write this:

    del msg['Bcc']
    

    (In case the dunder method is confusing, del a[b] translates to something like a.__delitem__(b) under the hood. The documentation does not make direct use of this syntax except when describing a cautionary note for its respective __setitem__() method)

    This code does effectively the same thing as your solution (in that the implementation mutates the internal _headers attribute too, and you can read it here). The good thing about this is, well, you don't have to reinvent the wheel (or access private variables! ;)

    In fact, the smtplib module uses this precise syntax when preparing an email message for transmission:

    class SMTP:
        def send_message(self, msg, from_addr=None, to_addrs=None,
                         mail_options=(), rcpt_options=()):
            ...
            # Make a local copy so we can delete the bcc headers.
            msg_copy = copy.copy(msg)
            del msg_copy['Bcc']
            del msg_copy['Resent-Bcc']
            ...
    

    There is also a 2016 answer here on Stack Overflow suggesting the same use of the delitem syntax. I suppose it is a well-established method based on that, despite not that many sources mentioning it as far as I can tell.