Skip to main content

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

Message 类非常类似于 EmailMessage 类,没有该类添加的方法,并且某些其他方法的默认行为略有不同。我们还在这里记录了一些方法,虽然 EmailMessage 类支持,不推荐,除非你在处理遗留代码。

两个类的哲学和结构在其他方面是一样的。

本文档描述了默认(对于 Message)策略 Compat32 的行为。如果你要使用另一个策略,你应该使用 EmailMessage 类。

电子邮件由 headerspayload 组成。标题必须是 RFC 5233 样式的名称和值,其中字段名称和值由冒号分隔。冒号不是字段名称或字段值的一部分。有效载荷可以是简单文本消息,或二进制对象或子消息的结构化序列,每个子消息具有它们自己的一组头部和它们自己的有效载荷。后一类型的有效载荷由具有诸如 multipart/*message/rfc822 的MIME类型的消息指示。

Message 对象提供的概念模型是头部的有序字典,具有用于从头部访问专用信息,用于访问有效载荷,用于生成mssage的序列化版本以及用于递归地遍历对象树的附加方法。请注意,支持重复的标头,但必须使用特殊方法来访问它们。

Message psuedo字典由标题名称索引,标题名称必须是ASCII值。字典的值是应该只包含ASCII字符的字符串;有一些特殊的处理非ASCII输入,但它并不总是产生正确的结果。头以字节保存的形式存储和返回,但字段名匹配大小写不敏感。还可以存在单个包络报头,也称为 Unix-From 报头或 From_ 报头。对于MIME容器文档(例如 multipart/*message/rfc822),payload 是在简单消息对象的情况下的字符串或字节,或者是 Message 对象的列表。

这里是 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

如果需要填充缺省值以完成到字符串的转换(例如,可能生成或修改MIME边界),则展平消息可能会触发对 Message 的更改。

请注意,此方法是为了方便而提供,可能并不总是按照您想要的方式格式化邮件。例如,默认情况下,它不会对以unix mbox格式所需的 From 开头的行进行修改。为了更灵活,实例化 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

如果需要填充缺省值以完成到字符串的转换(例如,可能生成或修改MIME边界),则展平消息可能会触发对 Message 的更改。

请注意,此方法是为了方便而提供,可能并不总是按照您想要的方式格式化邮件。例如,默认情况下,它不会对以unix mbox格式所需的 From 开头的行进行修改。为了更灵活,实例化 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。然而,对于8比特的 Content-Transfer-Encoding,尝试使用由 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,则将从 Content-Type 标题中移除 charset 参数(该消息不会被另外修改)。任何其他将生成 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-8bit 字符集的 Header 对象。

__len__()

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

__contains__(name)

如果消息对象有一个名为 name 的字段,则返回true。匹配不区分大小写,name 不应包含尾部冒号。用于 in 运营商,例如。:

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

返回指定头字段的值。 name 不应包含冒号字段分隔符。如果标题缺失,则返回 NoneKeyError 从不上升。

请注意,如果命名字段在消息的标头中多次出现,则不确定将返回哪些字段值。使用 get_all() 方法获取所有现有命名头的值。

__setitem__(name, val)

在字段名为 name 和值为 val 的消息中添加标题。该字段将追加到消息的现有字段的末尾。

请注意,这会使 not 覆盖或删除具有相同名称的任何现有标题。如果要确保新标头是消息中唯一存在的字段名为 name 的消息,请先删除该字段,例如。:

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

从邮件标头中删除名称为 name 的所有字段。如果指定的字段不存在于标头中,则不会引发异常。

keys()

返回所有消息的头字段名称的列表。

values()

返回所有消息的字段值的列表。

items()

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

get(name, failobj=None)

返回指定头字段的值。这与 __getitem__() 相同,除非如果指定的标头缺失(默认为 None),则返回可选的 failobj

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

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字符,则使用 utf-8CHARSETNoneLANGUAGE 自动将其编码为 RFC 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 (默认值),则该值为非引号。

如果没有 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 编码的,则返回3元组。当它是一个3元组,值的元素是 (CHARSET, LANGUAGE, VALUE) 的形式。注意,CHARSETLANGUAGE 都可以是 None,在这种情况下,您应该考虑 VALUEus-ascii 字符集中进行编码。你通常可以忽略 LANGUAGE

如果您的应用程序不关心参数是否编码为 RFC 2231 中,您可以通过调用 email.utils.collapse_rfc2231_value() 来折叠参数值,传入来自 get_param() 的返回值。当值为元组时,将返回一个适当解码的Unicode字符串,否则返回原始字符串unquoted。例如:

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 标题的顺序。然而,它确实 not 保留可能存在于原始 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() 方法是一种通用的生成器,可用于以深度优先遍历顺序遍历消息对象树的所有部分和子部分。您通常会在 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() 返回 Truewalk 下降到子部分。

Message 对象也可以可选地包含两个实例属性,可以在生成MIME消息的纯文本时使用。

preamble

MIME文档的格式允许在标题后的空行和第一个多部分边界字符串之间有一些文本。通常,此文本在MIME感知邮件阅读器中永远不可见,因为它不属于标准MIME装甲。但是,在查看邮件的原始文本时,或者在非MIME感知读取器中查看邮件时,此文本可能会变为可见。

preamble 属性包含MIME文档的此前导装甲文本。当 Parser 发现标题之后但在第一个边界字符串之前的一些文本时,它将此文本分配给消息的 preamble 属性。当 Generator 写出MIME消息的纯文本表示,并且它发现消息具有 preamble 属性时,它将在头部和第一边界之间的区域中写入该文本。详情请参阅 email.parseremail.generator

注意,如果消息对象没有前导码,则 preamble 属性将是 None

epilogue

epilogue 属性的行为方式与 preamble 属性相同,除了它包含出现在消息的最后边界和结尾之间的文本。

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

defects

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