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

Message 类与 EmailMessage 类非常相似,只是它没有该类添加的方法,并且某些其他方法的默认行为略有不同。我们还在这里记录了一些 EmailMessage 类支持的方法,除非您正在处理遗留代码,否则不建议使用它们。

这两个类的理念和结构其他方面是相同的。

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

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

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

Message 伪字典通过标头名称进行索引,标头名称必须是 ASCII 值。字典的值是应该仅包含 ASCII 字符的字符串;对于非 ASCII 输入有一些特殊处理,但它并不总是产生正确的结果。标头以保留大小写的形式存储和返回,但字段名称不区分大小写地匹配。还可能存在单个信封标头,也称为 Unix-From 标头或 From_ 标头。负载可以是字符串或字节(对于简单消息对象),也可以是 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 为 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 默认为 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) 生成一个包含格式化消息的字节对象。

在 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 标头指定的 charsetreplace 错误处理程序来解码原始字节。如果没有指定 charset,或者 email 包无法识别给定的 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 字节的标头值,通过此接口检索时,将表示为字符集为 unknown-8bitHeader 对象。

__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 字符,则会自动以 RFC 2231 格式编码,使用 utf-8CHARSETNoneLANGUAGE

这是一个示例

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 参数。返回的列表元素是键/值对的二元组,以 '=' 符号分割。 '=' 的左侧是键,右侧是值。如果参数中没有 '=' 符号,则值为空字符串,否则该值如 get_param() 中所述,如果可选的 unquoteTrue(默认值),则会进行取消引用。

如果没有 Content-Type 标头,则可选的 failobj 是要返回的对象。可选的 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)。请注意,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)

在任何情况下,参数值(无论是返回的字符串,还是三元组中的 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。此方法的可能值为 inlineattachmentNone(如果消息遵循 RFC 2183)。

在 3.5 版本中添加。

walk()

walk() 方法是一个通用的生成器,可用于以深度优先遍历顺序迭代消息对象树的所有部分和子部分。通常,您会在 for 循环中使用 walk() 作为迭代器;每次迭代返回下一个子部分。

这是一个打印多部分消息结构的每个部分的 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,则 preamble 属性将为 None

epilogue

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

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

defects

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