email.message.Message: представление сообщения электронной почты с использованием API compat32

Класс Message очень похож на класс EmailMessage, но без методов, добавленных этим классом, и с немного другим поведением по умолчанию некоторых других методов. Мы также документируем здесь некоторые методы, которые, хотя и поддерживаются классом EmailMessage, не рекомендуются, если вы не имеете дело с устаревшим кодом.

В остальном философия и структура двух классов одинаковы.

В документе определяется поведение по умолчанию (для Message) политики Compat32. Если вы собираетесь использовать другую политику, вместо этого следует использовать класс EmailMessage.

Сообщение электронной почты состоит из headers и payload. Заголовки должны иметь имена и значения в стиле RFC 5233, где имя поля и значение разделяются двоеточием. Двоеточие не является частью ни имени поля, ни значения поля. Полезная нагрузка может быть простым текстовым сообщением, двоичным объектом или структурированной последовательностью подсообщений, каждое из которых имеет собственный множество заголовков и собственную полезную нагрузку. Последний тип полезной нагрузки указывается сообщением, имеющим тип MIME, такой как multipart/* или message/rfc822.

Концептуальная модель, предоставляемая объектом Message, представляет собой упорядоченный словарь заголовков с дополнительными методами для доступа к специализированной информации из заголовков, для доступа к полезной нагрузке, для создания сериализованной версии сообщения и для рекурсивного обхода дерева объектов. . Обратите внимание, что дубликаты заголовков поддерживаются, но для доступа к ним необходимо использовать специальные методы.

Псевдословарь Message индексируется по именам заголовков, которые должны быть значениями ASCII. Значения словаря — это строки, которые должны содержать только символы ASCII; существует специальная обработка ввода, отличного от ASCII, но она не всегда предоставляет правильные результаты. Заголовки сохраняются и возвращаются в форме с сохранением регистра, но имена полей сопоставляются без учета регистра. Также может быть один заголовок конверта, также известный как заголовок Unix-From или заголовок From_. payload — это либо строка, либо байты в случае простых объектов сообщений или список объектов Message для документов-контейнеров MIME (например, multipart/* и message/rfc822).

Вот методы класса Message:

class email.message.Message(policy=compat32)

Если указан policy (это должен быть экземпляр класса policy), используйте указанные им правила для обновления и сериализации представления сообщения. Если policy не задан, используйте политику compat32, которая поддерживает обратную совместимость с версией пакета электронной почты Python 3.2. Дополнительные сведения см. в документации policy.

Изменено в версии 3.3: Добавлен ключевой аргумент policy.

as_string(unixfrom=False, maxheaderlen=0, policy=None)

Возвращает все сообщение в виде строки. Если необязательный unixfrom имеет значение истина, заголовок конверта включается в возвращаемую строку. unixfrom по умолчанию False. По соображениям обратной совместимости maxheaderlen по умолчанию имеет значение 0, поэтому, если вам нужно другое значение, вы должны явно переопределить его (значение, указанное для max_line_length в политике, будет игнорироваться этим методом). Аргумент policy может использоваться для переопределения политики по умолчанию, полученной из экземпляра сообщения. Это можно использовать для управления некоторым форматированием, создаваемым методом, поскольку указанный policy будет передан Generator.

Выравнивание сообщения может инициировать изменения в Message, если необходимо заполнить значения по умолчанию для завершения преобразования в строку (например, могут быть сгенерированы или изменены границы MIME).

Обратите внимание, что данный метод предоставляется для удобства и может не всегда форматировать сообщение так, как вы хотите. Например, по умолчанию он не искажает строки, начинающиеся с From, что требуется для формата unix mbox. Для большей гибкости создаёт экземпляр экземпляра Generator и напрямую используйте его метод flatten(). Например:

from io import StringIO
from email.generator import Generator
fp = StringIO()
g = Generator(fp, mangle_from_=True, maxheaderlen=60)
g.flatten(msg)
text = fp.getvalue()

Если объект сообщения содержит двоичные данные, которые не закодированы в соответствии со стандартами RFC, несоответствующие данные будут заменены кодовыми точками Юникод «неизвестный символ». (См. также as_bytes() и BytesGenerator.)

Изменено в версии 3.4: Добавлен ключевой аргумент policy.

__str__()

Эквивалент as_string(). Позволяет str(msg) создать строку, содержащую отформатированное сообщение.

as_bytes(unixfrom=False, policy=None)

Возвращает все сообщение в виде объекта bytes. Если необязательный unixfrom имеет истинное значение, заголовок конверта включается в возвращаемую строку. unixfrom по умолчанию False. Аргумент policy может использоваться для переопределения политики по умолчанию, полученной из экземпляра сообщения. Это можно использовать для управления некоторым форматированием, создаваемым методом, поскольку указанный policy будет передан BytesGenerator.

Выравнивание сообщения может инициировать изменения в Message, если необходимо заполнить значения по умолчанию для завершения преобразования в строку (например, могут быть сгенерированы или изменены границы MIME).

Обратите внимание, что данный метод предоставляется для удобства и может не всегда форматировать сообщение так, как вы хотите. Например, по умолчанию он не искажает строки, начинающиеся с From, что требуется для формата unix mbox. Для большей гибкости создаёт экземпляр экземпляра BytesGenerator и напрямую используйте его метод flatten(). Например:

from io import BytesIO
from email.generator import BytesGenerator
fp = BytesIO()
g = BytesGenerator(fp, mangle_from_=True, maxheaderlen=60)
g.flatten(msg)
text = fp.getvalue()

Добавлено в версии 3.4.

__bytes__()

Эквивалент as_bytes(). Позволяет bytes(msg) создать bytes объект, содержащий отформатированное сообщение.

Добавлено в версии 3.4.

is_multipart()

Возвращает True, если полезная нагрузка сообщения представляет собой список подобъектов Message, в противном случае возвращает False. Когда is_multipart() возвращает False, полезная нагрузка должна быть строковым объектом (который может быть двоичной полезной нагрузкой в кодировке CTE). (Обратите внимание, что is_multipart(), возвращающий True, не обязательно означает, что «msg.get_content_maintype() == „multipart“» вернёт True. Например, is_multipart вернёт True, когда Message имеет тип message/rfc822.)

set_unixfrom(unixfrom)

Устанавливает для заголовка конверта сообщения значение unixfrom, которое должно быть строкой.

get_unixfrom()

Возвращает заголовок конверта сообщения. По умолчанию None, если заголовок конверта никогда не устанавливался.

attach(payload)

Добавляет заданный payload к текущей полезной нагрузке, которая перед вызовом должна быть None или списком объектов Message. После вызова полезной нагрузкой всегда будет список объектов Message. Если вы хотите установить в качестве полезной нагрузки скалярный объект (например, строку), используйте вместо этого set_payload().

Это устаревший метод. В классе EmailMessage его функциональность заменена на set_content() и связанные методы make и add.

get_payload(i=None, decode=False)

Возвращает текущую полезную нагрузку, которая будет представлять собой список объектов Message, если is_multipart() — это True, или строку, если is_multipart() — это False. Если полезная нагрузка представляет собой список, и вы изменяете объект списка, вы изменяете полезную нагрузку сообщения на месте.

С необязательным аргументом i функция get_payload() вернёт i-й элемент полезной нагрузки, считая с нуля, если is_multipart() равен True. IndexError будет создан, если i меньше 0 или больше или равно количеству элементов в полезной нагрузке. Если полезная нагрузка представляет собой строку (т. е. is_multipart() — это False) и задан i, вызывается TypeError.

Необязательный decode — это флаг, указывающий, следует ли декодировать полезную нагрузку в соответствии с заголовком Content- Transfer-Encoding. Когда True и сообщение не является составным, полезная нагрузка будет декодирована, если значение этого заголовка равно quoted-printable или base64. Если используется какая-либо другая кодировка или заголовок Content-Transfer-Encoding отсутствует, полезная нагрузка возвращается как есть (незакодированная). Во всех случаях возвращаемое значение представляет собой двоичные данные. Если сообщение состоит из нескольких частей и флаг decode равен True, то возвращается None. Если полезная нагрузка имеет формат base64 и не была идеально сформирована (отсутствуют отступы, символы вне алфавита base64), то в свойство дефекта сообщения будет добавлен соответствующий дефект (InvalidBase64PaddingDefect или InvalidBase64CharactersDefect соответственно).

Когда decode равен False (по умолчанию), тело возвращается в виде строки без декодирования Content-Transfer-Encoding. Однако для 8-битного Content-Transfer-Encoding делается попытка декодировать исходные байты с использованием charset, указанного в заголовке Content-Type, с использованием обработчика ошибок replace. Если charset не указан или указанный charset не распознается почтовым пакетом, тело декодируется с использованием кодировки ASCII по умолчанию.

Это устаревший метод. В классе EmailMessage его функциональность заменена на get_content() и iter_parts().

set_payload(payload, charset=None)

Устанавливает для полезной нагрузки всего объекта сообщения значение payload. Ответственность за обеспечение неизменности полезной нагрузки лежит на клиенте. Необязательный charset устанавливает множество символов сообщения по умолчанию; подробности см. в set_charset().

Это устаревший метод. В классе EmailMessage его функциональность заменена на set_content().

set_charset(charset)

Устанавливает множество символов полезной нагрузки на charset, который может быть экземпляром Charset (см. email.charset), строкой, обозначающей множество символов, или None. Если это строка, она будет преобразована в экземпляр Charset. Если charset имеет значение None, параметр charset будет удален из заголовка Content-Type (в противном случае сообщение не будет изменено). Все остальное будет генерировать TypeError.

Если нет существующего заголовка MIME-Version, он будет добавлен. Если нет существующего заголовка Content-Type, он будет добавлен со значением text/plain. Независимо от того, существует заголовок Content-Type или нет, его параметру charset будет присвоено значение charset.output_charset. Если charset.input_charset и charset.output_charset различаются, полезная нагрузка будет перекодирована в output_charset. Если нет существующего заголовка Content-Transfer-Encoding, то при необходимости полезная нагрузка будет закодирована при передаче с использованием указанного Charset, и будет добавлен заголовок с соответствующим значением. Если заголовок Content-Transfer- Encoding уже существует, предполагается, что полезная нагрузка уже правильно закодирована с использованием этого Content- Transfer-Encoding и не изменяется.

Это устаревший метод. В классе EmailMessage его функциональность заменена параметром charset метода email.emailmessage.EmailMessage.set_content().

get_charset()

Возвращает экземпляр Charset, связанный с полезной нагрузкой сообщения.

Это устаревший метод. В классе EmailMessage он всегда возвращает None.

Следующие методы реализуют интерфейс, похожий на сопоставление, для доступа к заголовкам сообщения RFC 2822. Обратите внимание, что существуют некоторые семантические различия между этими методами и интерфейсом сопоставления нормалей (т. е. словаря). Например, в словаре нет дубликатов ключей, а здесь могут быть дубликаты заголовков сообщений. Кроме того, в словарях нет гарантированного порядка ключей, возвращаемых keys(), но в объекте Message заголовки всегда возвращаются в том порядке, в котором они появились в исходном сообщении или были добавлены в сообщение позже. Любой удаленный, а затем повторно добавленный заголовок всегда добавляется в конец списка заголовков.

Данные семантические различия являются преднамеренными и направлены на максимальное удобство.

Обратите внимание, что во всех случаях любой заголовок конверта, присутствующий в сообщении, не включается в интерфейс сопоставления.

В модели, сгенерированной из байтов, любые значения заголовков, которые (в нарушение RFC) содержат байты, отличные от ASCII, при извлечении через данный интерфейс будут представлены как объекты Header с кодировкой unknown-8bit.

__len__()

Возвращает общее количество заголовков, включая дубликаты.

__contains__(name)

Возвращает True, если объект сообщения имеет поле с именем name. Сопоставление выполняется без учета регистра, и name не должен включать двоеточие в конце. Используется для оператора in, например.:

if 'message-id' in myMessage:
   print('Message-ID:', myMessage['message-id'])
__getitem__(name)

Возвращает значение именованного поля заголовка. name не должен включать разделитель полей двоеточия. Если заголовок отсутствует, возвращается None; KeyError никогда не вызывается.

Обратите внимание, что если именованное поле появляется в заголовках сообщения более одного раза, то, какие именно значения данных полей будут возвращены, не определено. Используйте метод get_all(), чтобы получить значения всех существующих именованных заголовков.

__setitem__(name, val)

Добавляет в сообщение заголовок с именем поля name и значением val. Поле добавляется в конец существующих полей сообщения.

Обратите внимание, что не перезаписывает или удаляет любой существующий заголовок с тем же именем. Если вы хотите, чтобы в сообщении с именем поля name присутствовал только новый заголовок, сначала удалить это поле, например.:

del msg['subject']
msg['subject'] = 'Python roolz!'
__delitem__(name)

Удалить все вхождения поля с именем name из заголовков сообщения. Исключение не вызывается, если именованное поле отсутствует в заголовках.

keys()

Возвращает список всех имён полей заголовка сообщения.

values()

Возвращает список всех значений полей сообщения.

items()

Возвращает список из двух кортежей, содержащих все заголовки и значения полей сообщения.

get(name, failobj=None)

Возвращает значение именованного поля заголовка. Это идентично __getitem__(), за исключением того, что необязательный failobj возвращается, если именованный заголовок отсутствует (по умолчанию None).

Вот ещё несколько полезных методов:

get_all(name, failobj=None)

Возвращает список всех значений для поля с именем name. Если в сообщении нет таких именованных заголовков, возвращается failobj (по умолчанию None).

add_header(_name, _value, **_params)

Расширенная настройка заголовка. Данный метод аналогичен __setitem__(), за исключением того, что дополнительные параметры заголовка могут быть предоставлены в качестве ключевых аргументов. _name — это поле заголовка для добавления, а _value — это значение primary для заголовка.

Для каждого элемента в словаре ключевых аргументов _params в качестве имени параметра используется ключ, а символы подчеркивания преобразуются в тире (поскольку тире недопустимы в идентификаторах Python). Обычно параметр будет добавлен как key="value", если только значение не равно None, и в этом случае будет добавлен только ключ. Если значение содержит не-ASCII-символы, его можно указать в виде трёх кортежей в формате (CHARSET, LANGUAGE, VALUE), где CHARSET — это строка с именем набора символов, который будет использоваться для кодирования значения, LANGUAGE обычно может быть установлен на None или пустую строку ( см. RFC 2231 для других возможностей), а VALUE — это строковое значение, содержащее кодовые точки, отличные от ASCII. Если три кортежа не переданы и значение содержит символы, отличные от ASCII, оно автоматически кодируется в формате RFC 2231 с использованием CHARSET из utf-8 и LANGUAGE из None.

Вот пример:

msg.add_header('Content-Disposition', 'attachment', filename='bud.gif')

Это добавит заголовок, который выглядит как

Content-Disposition: attachment; filename="bud.gif"

Пример с символами, отличными от ASCII:

msg.add_header('Content-Disposition', 'attachment',
               filename=('iso-8859-1', '', 'Fußballer.ppt'))

Который производит:

Content-Disposition: attachment; filename*="iso-8859-1''Fu%DFballer.ppt"
replace_header(_name, _value)

Заменить заголовок. Заменить первый заголовок, найденный в сообщении, который соответствует _name, сохранив порядок заголовков и регистр имени поля. Если соответствующий заголовок не найден, выдается KeyError.

get_content_type()

Возвращает тип содержимого сообщения. Возвращаемая строка приводится к нижнему регистру формы maintype/subtype. Если в сообщении не было заголовка Content-Type, будет возвращён тип по умолчанию, заданный get_default_type(). Поскольку в соответствии с RFC 2045 сообщения всегда имеют тип по умолчанию, get_content_type() всегда будет возвращать значение.

RFC 2045 определяет тип сообщения по умолчанию как text/plain, если только оно не появляется внутри контейнера multipart/digest, в этом случае это будет message/rfc822. Если заголовок Content-Type имеет недопустимую спецификацию типа, RFC 2045 требует, чтобы тип по умолчанию был text/plain.

get_content_maintype()

Возвращает основной тип содержимого сообщения. Это часть строки maintype, возвращаемая get_content_type().

get_content_subtype()

Возвращает тип подсодержимого сообщения. Это часть subtype строки, возвращаемой get_content_type().

get_default_type()

Возвращает тип контента по умолчанию. Большинство сообщений имеют тип содержимого по умолчанию text/plain, за исключением сообщений, являющихся частями контейнеров multipart/digest. Такие части имеют тип содержимого по умолчанию message/rfc822.

set_default_type(ctype)

Устанавливает тип содержимого по умолчанию. ctype должен быть либо text/plain, либо message/rfc822, хотя это не обязательно. Тип контента по умолчанию не хранится в заголовке Content-Type.

get_params(failobj=None, header='content-type', unquote=True)

Возвращает параметры сообщения Content-Type в виде списка. Элементы возвращаемого списка представляют собой 2 кортежа пар ключ/значение, разделенных по знаку '='. Левая часть '=' — это ключ, а правая — значение. Если в параметре нет знака '=', значение представляет собой пустую строку, в противном случае значение соответствует описанию в get_param() и не заключено в кавычки, если необязательный unquote равен True (по умолчанию).

Необязательный failobj — это возвращаемый объект, если заголовок Content-Type отсутствует. Необязательный заголовок header для поиска вместо Content-Type.

Это устаревший метод. В классе EmailMessage его функциональность заменена свойством params отдельных объектов заголовков, возвращаемых методами доступа к заголовку.

get_param(param, failobj=None, header='content-type', unquote=True)

Возвращает значение параметра заголовка Content-Type param в виде строки. Если сообщение не имеет заголовка Content-Type или такого параметра нет, то возвращается failobj (по умолчанию None).

Необязательный header, если задан, указывает заголовок сообщения, который следует использовать вместо Content-Type.

Ключи параметров всегда сравниваются без учета регистра. Возвращаемое значение может быть либо строкой, либо тройкой, если параметр имеет кодировку RFC 2231. Когда это тройка, элементы значения имеют вид (CHARSET, LANGUAGE, VALUE). Обратите внимание, что CHARSET и LANGUAGE могут быть None, и в этом случае вы должны считать, что VALUE закодирован в кодировке us-ascii. Обычно вы можете игнорировать LANGUAGE.

Если вашему приложению все равно, был ли параметр закодирован как RFC 2231, вы можете свернуть значение параметра, вызвав email.utils.collapse_rfc2231_value(), передав возвращаемое значение из get_param(). Это вернёт подходящим образом декодированную строку Юникод, если значение является кортежем, или исходную строку без кавычек, если это не так. Например:

rawparam = msg.get_param('foo')
param = email.utils.collapse_rfc2231_value(rawparam)

В любом случае значение параметра (либо возвращаемая строка, либо элемент VALUE в 3-кортеже) всегда не заключено в кавычки, если для unquote не задано значение False.

Это устаревший метод. В классе EmailMessage его функциональность заменена свойством params отдельных объектов заголовков, возвращаемых методами доступа к заголовку.

set_param(param, value, header='Content-Type', requote=True, charset=None, language='', replace=False)

Устанавливает параметр в заголовке Content-Type. Если параметр уже существует в заголовке, его значение будет заменено на value. Если заголовок Content-Type ещё не определён для этого сообщения, он будет установлен на text/plain, а новое значение параметра будет добавлено в соответствии с RFC 2045.

Необязательный header указывает заголовок, альтернативный Content-Type, и все параметры будут заключены в кавычки по мере необходимости, если необязательный requote не равен False (по умолчанию True).

Если указан необязательный charset, параметр будет закодирован в соответствии с RFC 2231. Необязательный language указывает язык RFC 2231, по умолчанию используется пустая строка. И charset, и language должны быть строками.

Если replace равен False (по умолчанию), заголовок перемещается в конец списка заголовков. Если replace равен True, заголовок будет обновлен на месте.

Изменено в версии 3.4: replace keyword was added.

del_param(param, header='content-type', requote=True)

Полностью удалить данный параметр из заголовка Content- Type. Заголовок будет перезаписан на месте без параметра или его значения. Все значения будут указаны по мере необходимости, если requote не равно False (по умолчанию True). Необязательный header указывает альтернативу Content-Type.

set_type(type, header='Content-Type', requote=True)

Устанавливает основной тип и подтип для заголовка Content- Type. type должен быть строкой в форме maintype/subtype, в противном случае вызывается ValueError.

Данный метод заменяет заголовок Content-Type, сохраняя все параметры на месте. Если requote имеет значение False, цитирование существующего заголовка остаётся без изменений, в противном случае параметры будут заключены в кавычки (по умолчанию).

Альтернативный заголовок можно указать в аргументе header. Когда установлен заголовок Content-Type, также добавляется заголовок MIME-Version.

Это устаревший метод. В классе EmailMessage его функциональность заменена методами make_ и add_.

get_filename(failobj=None)

Возвращает значение параметра filename заголовка Content-Disposition сообщения. Если в заголовке нет параметра filename, данный метод возвращается к поиску параметра name в заголовке Content-Type. Если ни один из них не найден или заголовок отсутствует, возвращается failobj. Возвращаемая строка всегда будет без кавычек в соответствии с email.utils.unquote().

get_boundary(failobj=None)

Возвращает значение параметра boundary заголовка Content-Type сообщения или failobj, если заголовок отсутствует или не имеет параметра boundary. Возвращаемая строка всегда будет без кавычек в соответствии с email.utils.unquote().

set_boundary(boundary)

Устанавливает для параметра boundary заголовка Content- Type значение boundary. set_boundary() всегда будет указывать boundary, если это необходимо. HeaderParseError вызывается, если объект сообщения не имеет заголовка Content-Type.

Обратите внимание, что использование этого метода несколько отличается от удаления старого заголовка Content-Type и добавления нового с новой границей через add_header(), поскольку set_boundary() сохраняет порядок заголовка Content-Type в списке заголовков. Однако not сохраняет любые строки продолжения, которые могли присутствовать в исходном заголовке Content-Type.

get_content_charset(failobj=None)

Возвращает параметр charset заголовка Content-Type, приведённый к нижнему регистру. Если заголовок Content-Type отсутствует или данный заголовок не имеет параметра charset, возвращается failobj.

Обратите внимание, что данный метод отличается от get_charset(), который возвращает экземпляр Charset для кодировки тела сообщения по умолчанию.

get_charsets(failobj=None)

Возвращает список, содержащий имена наборов символов в сообщении. Если сообщение имеет вид multipart, то список будет содержать по одному элементу для каждой части полезной нагрузки, в противном случае это будет список длиной 1.

Каждый элемент в списке будет строкой, которая является значением параметра charset в заголовке Content-Type для представляемой части. Однако если подчасть не имеет заголовка Content-Type, параметра charset или не имеет основного типа MIME text, то данный элемент в возвращаемом списке будет failobj.

get_content_disposition()

Возвращает значение в нижнем регистре (без параметров) заголовка сообщения Content-Disposition, если он есть, или None. Возможные значения для этого метода: inline, attachment или None, если сообщение следует за RFC 2183.

Добавлено в версии 3.5.

walk()

Метод walk() — универсальный генератор, который можно использовать для перебора всех частей и подчастей дерева объектов сообщений в порядке обхода в глубину. Обычно вы будете использовать walk() в качестве итератора в цикле for; каждая итерация возвращает следующую часть.

Вот пример, который печатает MIME-тип каждой части составной структуры сообщения:

>>> for part in msg.walk():
...     print(part.get_content_type())
multipart/report
text/plain
message/delivery-status
text/plain
text/plain
message/rfc822
text/plain

walk перебирает части любой части, где is_multipart() возвращает True, даже если msg.get_content_maintype() == 'multipart' может возвращать False. Мы можем увидеть это в нашем примере, используя вспомогательную функцию отладки _structure:

>>> for part in msg.walk():
...     print(part.get_content_maintype() == 'multipart',
...           part.is_multipart())
True True
False False
False True
False False
False False
False True
False False
>>> _structure(msg)
multipart/report
    text/plain
    message/delivery-status
        text/plain
        text/plain
    message/rfc822
        text/plain

Здесь части message не являются multiparts, но содержат подчасти. is_multipart() возвращает True, а walk переходит в подчасти.

Объекты Message также могут дополнительно содержать два атрибута экземпляра, которые можно использовать при создании простого текста сообщения MIME.

preamble

Формат документа MIME позволяет размещать некоторый текст между пустой строкой, следующей за заголовками, и первой составной граничной строкой. Обычно данный текст никогда не отображается в программе чтения почты с поддержкой MIME, потому что он выходит за рамки стандартной защиты MIME. Однако при просмотре необработанного текста сообщения или при просмотре сообщения в программе чтения, не поддерживающей MIME, данный текст может стать видимым.

Атрибут preamble содержит данный начальный дополнительный текст для документов MIME. Когда Parser обнаруживает некоторый текст после заголовков, но до первой граничной строки, он присваивает данный текст атрибуту сообщения preamble. Когда Generator записывает простое текстовое представление сообщения MIME и обнаруживает, что сообщение имеет атрибут preamble, он записывает данный текст в область между заголовками и первой границей. Подробности см. в email.parser и email.generator.

Обратите внимание, что если объект сообщения не имеет преамбулы, атрибут preamble будет иметь значение None.

epilogue

Атрибут epilogue действует так же, как атрибут preamble, за исключением того, что он содержит текст, который появляется между последней границей и концом сообщения.

Вам не нужно устанавливать в эпилоге пустую строку, чтобы Generator печатал новую строку в конце файла.

defects

Атрибут defects содержит список всех проблем, обнаруженных при разборе этого сообщения. Подробное описание возможных дефектов анализа см. в email.errors.