email.message.Message: 使用 compat32 API 表示电子邮件消息

Message 类与 EmailMessage 类非常相似,只是没有该类添加的方法,并且某些其他方法的默认行为略有不同。我们还将在本文档中介绍一些方法,尽管这些方法受 EmailMessage 类支持,但除非您正在处理遗留代码,否则不建议使用它们。

这两个类的理念和结构是相同的。

本文档描述了在默认(对于 Message)策略 Compat32 下的行为。如果您要使用其他策略,则应使用 EmailMessage 类。

电子邮件消息由标题有效负载组成。标题必须是 RFC 5322 样式的名称和值,其中字段名称和值用冒号分隔。冒号不属于字段名称或字段值的一部分。有效负载可以是简单的文本消息,也可以是二进制对象,或者是一系列结构化的子消息,每个子消息都有自己的标题集和自己的有效负载。后一种类型的有效负载由消息具有 MIME 类型(如 multipart/*message/rfc822)来指示。

Message 对象提供的概念模型是标题的有序字典,以及用于访问标题中的专用信息、访问有效负载、生成消息的序列化版本以及递归遍历对象树的其他方法。请注意,支持重复标题,但必须使用特殊方法来访问它们。

Message 伪字典按标题名称索引,标题名称必须是 ASCII 值。字典的值是字符串,这些字符串应该只包含 ASCII 字符;对于非 ASCII 输入,有一些特殊处理,但并不总是产生正确的结果。标题以保留大小写的方式存储和返回,但字段名称匹配不区分大小写。还可能存在一个单独的信封标题,也称为Unix-From 标题或 From_ 标题。有效负载对于简单消息对象,可以是字符串或字节,对于 MIME 容器文档(例如 multipart/*message/rfc822),可以是 Message 对象的列表。

以下是 Message 类的所有方法。

class email.message.Message(policy=compat32)

如果指定了policy(它必须是 policy 类的实例),则使用它指定的规则来更新和序列化消息的表示形式。如果未设置policy,则使用 compat32 策略,该策略保持与 Python 3.2 版本的 email 包的向后兼容性。有关更多信息,请参阅 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 标准编码的二进制数据,则不符合标准的数据将被替换为 unicode“未知字符”代码点。(另请参阅 as_bytes()BytesGenerator。)

在 3.4 版中变更: 添加了policy 关键字参数。

__str__()

等效于 as_string()。允许 str(msg) 生成包含格式化消息的字符串。

as_bytes(unixfrom=False, policy=None)

将整个消息展平为字节对象并返回。当可选的unixfrom 为真时,信封标题将包含在返回的字符串中。unixfrom 默认值为 Falsepolicy 参数可用于覆盖从消息实例获得的默认策略。这可用于控制该方法生成的一些格式,因为指定的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) 生成包含格式化消息的字节对象。

在版本 3.4 中添加。

is_multipart()

如果消息的有效负载是子 Message 对象的列表,则返回 True,否则返回 False。当 is_multipart() 返回 False 时,有效负载应为字符串对象(它可能是 CTE 编码的二进制有效负载)。(请注意,is_multipart() 返回 True 并不一定意味着“msg.get_content_maintype() == ‘multipart’” 将返回 True。例如,当 Message 的类型为 message/rfc822 时,is_multipart 将返回 True。)

set_unixfrom(unixfrom)

将消息的信封头设置为 unixfrom,它应该是一个字符串。

get_unixfrom()

返回消息的信封头。如果从未设置信封头,则默认为 None

attach(payload)

将给定的 payload 添加到当前有效负载,在调用之前,当前有效负载必须为 NoneMessage 对象的列表。调用后,有效负载将始终为 Message 对象的列表。如果您想将有效负载设置为标量对象(例如字符串),请改用 set_payload()

这是一个遗留方法。在 EmailMessage 类中,其功能被 set_content() 和相关的 makeadd 方法取代。

get_payload(i=None, decode=False)

返回当前有效负载,当 is_multipart()True 时,它将是 Message 对象的列表,或者当 is_multipart()False 时,它将是字符串。如果有效负载是列表,并且您修改了列表对象,那么您将就地修改消息的有效负载。

使用可选参数 iget_payload() 将返回有效负载的第 i 个元素(从零开始计数),如果 is_multipart()True。如果 i 小于 0 或大于或等于有效负载中项目的数量,则会引发 IndexError。如果有效负载是字符串(即 is_multipart()False)并且给出了 i,则会引发 TypeError

可选的 decode 是一个标志,指示是否应根据 Content-Transfer-Encoding 头解码有效负载。当为 True 且消息不是多部分时,如果此头的值为 quoted-printablebase64,则将解码有效负载。如果使用其他编码,或者缺少 Content-Transfer-Encoding 头,则有效负载将按原样返回(未解码)。在所有情况下,返回值都是二进制数据。如果消息是多部分并且 decode 标志为 True,则返回 None。如果有效负载是 base64 并且它没有完全形成(缺少填充,字符不在 base64 字母表中),则将相应的缺陷添加到消息的缺陷属性中(分别为 InvalidBase64PaddingDefectInvalidBase64CharactersDefect)。

decodeFalse(默认值)时,主体将作为字符串返回,而不会解码 Content-Transfer-Encoding。但是,对于 Content-Transfer-Encoding 为 8bit,将尝试使用 Content-Type 头指定的 charset 解码原始字节,使用 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实例。 如果charsetNone,则charset参数将从Content-Type标头中删除(消息不会被修改)。 任何其他内容都会生成TypeError

如果没有现有的MIME-Version标头,则会添加一个。 如果没有现有的Content-Type标头,则会添加一个,其值为text/plain。 无论Content-Type标头是否已存在,其charset参数都将设置为charset.output_charset。 如果charset.input_charsetcharset.output_charset不同,则有效负载将被重新编码为output_charset。 如果没有现有的Content-Transfer-Encoding标头,则有效负载将根据需要使用指定的Charset进行传输编码,并且会添加一个具有相应值的标头。 如果Content-Transfer-Encoding标头已存在,则假定有效负载已使用该Content-Transfer-Encoding正确编码,并且不会被修改。

这是一个遗留方法。 在EmailMessage类中,其功能被email.emailmessage.EmailMessage.set_content()方法的charset参数取代。

get_charset()

返回与消息有效负载关联的Charset实例。

这是一个遗留方法。 在EmailMessage类中,它始终返回None

以下方法实现了用于访问消息的RFC 2822标头的映射式接口。 请注意,这些方法与普通映射(即字典)接口之间存在一些语义差异。 例如,在字典中没有重复的键,但这里可能存在重复的消息标头。 此外,在字典中,keys()返回的键没有保证的顺序,但在Message对象中,标头始终按其在原始消息中出现的顺序或后来添加到消息中的顺序返回。 任何被删除然后重新添加的标头始终附加到标头列表的末尾。

这些语义差异是故意的,并且偏向于最大程度的便利性。

请注意,在所有情况下,消息中存在的任何信封标头都不包含在映射接口中。

在从字节生成的模型中,任何标头值(违反 RFC)包含非 ASCII 字节,当通过此接口检索时,将表示为Header对象,其字符集为unknown-8bit

__len__()

返回标头的总数,包括重复项。

__contains__(name)

如果消息对象具有名为name的字段,则返回True。 匹配不区分大小写,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()

返回包含消息所有字段标头和值的 2 元组列表。

get(name, failobj=None)

返回命名标头字段的值。 这与__getitem__()相同,只是如果命名标头丢失,则返回可选的failobj(默认为None)。

以下是一些其他有用的方法

get_all(name, failobj=None)

返回名为name的字段的所有值的列表。 如果消息中没有这样的命名标头,则返回failobj(默认为None)。

add_header(_name, _value, **_params)

扩展标头设置。 此方法类似于__setitem__(),只是可以提供额外的标头参数作为关键字参数。 _name是要添加的标头字段,_value是标头的主要值。

对于关键字参数字典_params中的每个项目,键将被用作参数名称,下划线将被转换为连字符(因为连字符在 Python 标识符中是非法的)。 通常,参数将被添加为key="value",除非值为None,在这种情况下,只会添加键。 如果值包含非 ASCII 字符,则可以将其指定为三元组,格式为(CHARSET, LANGUAGE, VALUE),其中CHARSET是用于对值进行编码的字符集的名称,LANGUAGE通常可以设置为None或空字符串(参见RFC 2231以了解其他可能性),而VALUE是包含非 ASCII 代码点的字符串值。 如果没有传递三元组,并且值包含非 ASCII 字符,则它将使用CHARSETutf-8LANGUAGENoneRFC 2231格式自动编码。

以下是一个示例

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()

返回消息的 MIME 类型。返回的字符串被强制转换为 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()

返回消息的主 MIME 类型。这是由 get_content_type() 返回的字符串的 maintype 部分。

get_content_subtype()

返回消息的子 MIME 类型。这是由 get_content_type() 返回的字符串的 subtype 部分。

get_default_type()

返回默认 MIME 类型。大多数消息的默认 MIME 类型为 text/plain,除了作为 multipart/digest 容器的子部分的消息。此类子部分的默认 MIME 类型为 message/rfc822

set_default_type(ctype)

设置默认 MIME 类型。ctype 应为 text/plainmessage/rfc822,尽管这没有强制执行。默认 MIME 类型不会存储在 Content-Type 头中。

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

返回消息的 Content-Type 参数,作为列表。返回列表的元素是键/值对的 2 元组,以 '=' 符号分割。'=' 的左侧是键,而右侧是值。如果参数中没有 '=' 符号,则值为空字符串,否则值为 get_param() 中所述,如果可选的 unquoteTrue(默认值),则取消引用。

可选的 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

参数键始终不区分大小写进行比较。返回值可以是字符串,也可以是 3 元组(如果参数是 RFC 2231 编码的)。当它是 3 元组时,值的元素形式为 (CHARSET, LANGUAGE, VALUE)。请注意,CHARSETLANGUAGE 都可以是 None,在这种情况下,您应该将 VALUE 视为以 us-ascii 字符集编码。您通常可以忽略 LANGUAGE

如果您的应用程序不关心参数是否按 RFC 2231 编码,您可以通过调用 email.utils.collapse_rfc2231_value()(将 get_param() 的返回值传递进去)来折叠参数值。当值为元组时,这将返回一个适当解码的 Unicode 字符串,或者如果它不是元组,则返回原始字符串(取消引用)。例如

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

在任何情况下,参数值(返回的字符串或 3 元组中的 VALUE 项目)始终取消引用,除非 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 的备用头,并且所有参数都将根据需要进行引用,除非可选的 requoteFalse(默认值为 True)。

如果指定了可选的 charset,则参数将根据 RFC 2231 进行编码。可选的 language 指定 RFC 2231 语言,默认为空字符串。charsetlanguage 都应该是字符串。

如果 replaceFalse(默认值),则头将被移到头列表的末尾。如果 replaceTrue,则头将在原地更新。

版本 3.4 中的变更: replace 关键字已添加。

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

Content-Type 标头中完全删除给定的参数。标头将在不包含参数或其值的情况下就地重写。除非 requoteFalse(默认值为 True),否则所有值都将根据需要进行引用。

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

设置 Content-Type 标头的主要类型和子类型。type 必须是 maintype/subtype 形式的字符串,否则将引发 ValueError

此方法替换 Content-Type 标头,保留所有参数。如果 requoteFalse,则保留现有标头的引用方式,否则将引用参数(默认值)。

可以在 header 参数中指定备用标头。设置 Content-Type 标头时,还会添加 MIME-Version 标头。

这是一个遗留方法。在 EmailMessage 类中,其功能被 make_add_ 方法取代。

get_filename(failobj=None)

返回消息的 Content-Disposition 标头的 filename 参数的值。如果标头没有 filename 参数,则此方法将回退到查找 Content-Type 标头的 name 参数。如果两者都未找到,或者标头丢失,则返回 failobj。返回的字符串将始终根据 email.utils.unquote() 进行取消引用。

get_boundary(failobj=None)

返回消息的 Content-Type 标头的 boundary 参数的值,如果标头丢失或没有 boundary 参数,则返回 failobj。返回的字符串将始终根据 email.utils.unquote() 进行取消引用。

set_boundary(boundary)

Content-Type 标头的 boundary 参数设置为 boundary。如果需要,set_boundary() 将始终引用 boundary。如果消息对象没有 Content-Type 标头,则会引发 HeaderParseError

请注意,使用此方法与通过 add_header() 删除旧的 Content-Type 标头并添加具有新边界的新的标头略有不同,因为 set_boundary() 保留了标头列表中 Content-Type 标头的顺序。但是,它不会保留原始 Content-Type 标头中可能存在的任何续行。

get_content_charset(failobj=None)

返回 Content-Type 标头的 charset 参数,强制转换为小写。如果没有 Content-Type 标头,或者该标头没有 charset 参数,则返回 failobj

请注意,此方法与 get_charset() 不同,后者返回消息正文的默认编码的 Charset 实例。

get_charsets(failobj=None)

返回包含消息中字符集名称的列表。如果消息是 multipart,则列表将包含每个子部分的一个元素,否则将是长度为 1 的列表。

列表中的每个项目都将是一个字符串,它是表示子部分的 Content-Type 标头中 charset 参数的值。但是,如果子部分没有 Content-Type 标头,没有 charset 参数,或者不是 text 主要 MIME 类型,则返回列表中的该项目将是 failobj

get_content_disposition()

如果消息具有 Content-Disposition 标头,则返回其小写值(不带参数),否则返回 None。此方法的可能值为 inlineattachmentNone,如果消息遵循 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.parseremail.generator

请注意,如果消息对象没有序言,则 preamble 属性将为 None

epilogue

epilogue 属性的行为与 preamble 属性相同,只是它包含出现在最后一个边界和消息末尾之间的文本。

您不需要将 epilogue 设置为空字符串,以便 Generator 在文件末尾打印换行符。

defects

defects 属性包含解析此消息时发现的所有问题的列表。有关可能的解析缺陷的详细说明,请参阅 email.errors