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 为 true 时,返回的字符串中包含信封头部。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 为 true 时,返回的字符串中包含信封头部。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 时,它将是一个字符串。如果有效负载是列表并且您修改了列表对象,则您将就地修改消息的有效负载。

使用可选参数 i,如果 is_multipart()Trueget_payload() 将返回有效负载的第 i 个元素,从零开始计数。如果 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。但是,对于 8bit 的 Content-Transfer-Encoding,尝试使用 Content-Type 头部指定的 charset 解码原始字节,并使用 replace 错误处理程序。如果未指定 charset,或者给定的 charset 不被 email 包识别,则使用默认的 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.message.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 字符,则会自动使用 utf-8CHARSETNoneLANGUAGERFC 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()

返回消息的内容类型。返回的字符串将转换为小写形式 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()

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

get_content_subtype()

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

get_default_type()

返回默认内容类型。大多数消息的默认内容类型是 text/plain,但 multipart/digest 容器的子部分除外。此类子部分的默认内容类型是 message/rfc822

set_default_type(ctype)

设置默认内容类型。ctype 应该是 text/plainmessage/rfc822,尽管这并非强制执行。默认内容类型不存储在 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,在这种情况下,您应该认为 VALUEus-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),否则所有值都将根据需要引用。可选参数 header 指定 Content-Type 的替代头部。

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 参数设置为 boundaryset_boundary() 将始终在必要时引用 boundary。如果消息对象没有 Content-Type 头部,则会引发 HeaderParseError

请注意,使用此方法与删除旧的 Content-Type 头部并通过 add_header() 添加带有新边界的新头部略有不同,因为 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。如果消息遵循 RFC 2183,此方法的可能值是 inlineattachmentNone

在 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