email.policy:策略对象

在 3.3 版本加入。

源代码: Lib/email/policy.py


email 包的主要关注点是处理各种电子邮件和 MIME RFCs 所描述的电子邮件消息。然而,电子邮件消息的通用格式(一个由多个标头字段组成的块,每个字段包含一个名称后跟一个冒号,然后是一个值,整个块后跟一个空行和一个任意的“正文”)这种格式在电子邮件领域之外也得到了应用。其中一些用法与主要的电子邮件 RFCs 相当接近,而另一些则不然。即使在处理电子邮件时,有时也需要打破对 RFCs 的严格遵守,例如生成与那些本身不遵循标准或以违反标准的方式实现您想使用的扩展的电子邮件服务器互操作的电子邮件。

策略对象赋予 email 包处理所有这些不同用例的灵活性。

Policy 对象封装了一组属性和方法,用于在使用过程中控制 email 包中各种组件的行为。Policy 实例可以传递给 email 包中的各种类和方法,以改变默认行为。可设置的值及其默认值将在下面描述。

email 包中所有的类都使用一个默认策略。对于所有的 parser 类和相关的便利函数,以及 Message 类,默认策略是 Compat32 策略,通过其对应的预定义实例 compat32 来实现。这个策略提供了与 Python 3.3 之前的 email 包版本的完全向后兼容性(在某些情况下,包括与 bug 的兼容性)。

EmailMessagepolicy 关键字的默认值是 EmailPolicy 策略,通过其预定义实例 default 实现。

当创建 MessageEmailMessage 对象时,它会获得一个策略。如果消息是由 parser 创建的,传递给解析器的策略将成为它创建的消息所使用的策略。如果消息是由程序创建的,那么可以在创建时指定策略。当消息被传递给 generator 时,生成器默认使用消息的策略,但你也可以传递一个特定的策略给生成器,它将覆盖存储在消息对象上的策略。

在 Python 的未来版本中,email.parser 类和解析器便利函数的 policy 关键字的默认值**将会改变**。因此,在调用 parser 模块中描述的任何类和函数时,你应该**总是显式指定你想要使用的策略**。

本文档的第一部分涵盖了 Policy 的特性,它是一个抽象基类,定义了所有策略对象(包括 compat32)共有的特性。这包括一些由 email 包内部调用的钩子方法,自定义策略可以重写这些方法以获得不同的行为。第二部分描述了具体的类 EmailPolicyCompat32,它们分别实现了提供标准行为和向后兼容行为与特性的钩子。

Policy 实例是不可变的,但可以被克隆,接受与类构造函数相同的关键字参数,并返回一个新的 Policy 实例,该实例是原始实例的副本,但指定的属性值已被更改。

例如,以下代码可用于从磁盘文件中读取电子邮件消息,并将其传递给 Unix 系统上的系统 sendmail 程序。

>>> from email import message_from_binary_file
>>> from email.generator import BytesGenerator
>>> from email import policy
>>> from subprocess import Popen, PIPE
>>> with open('mymsg.txt', 'rb') as f:
...     msg = message_from_binary_file(f, policy=policy.default)
...
>>> p = Popen(['sendmail', msg['To'].addresses[0]], stdin=PIPE)
>>> g = BytesGenerator(p.stdin, policy=msg.policy.clone(linesep='\r\n'))
>>> g.flatten(msg)
>>> p.stdin.close()
>>> rc = p.wait()

在这里,我们告诉 BytesGenerator 在创建要输入到 sendmailstdin 的二进制字符串时,使用符合 RFC 规范的行分隔符,而默认策略会使用 \n 行分隔符。

一些 email 包的方法接受一个 policy 关键字参数,允许为该方法覆盖策略。例如,以下代码使用前一个示例中 msg 对象的 as_bytes() 方法,并使用其运行平台的原生行分隔符将消息写入文件。

>>> import os
>>> with open('converted.txt', 'wb') as f:
...     f.write(msg.as_bytes(policy=msg.policy.clone(linesep=os.linesep)))
17

策略对象也可以使用加法运算符进行组合,生成一个策略对象,其设置是相加对象非默认值的组合。

>>> compat_SMTP = policy.compat32.clone(linesep='\r\n')
>>> compat_strict = policy.compat32.clone(raise_on_defect=True)
>>> compat_strict_SMTP = compat_SMTP + compat_strict

这个操作不满足交换律;也就是说,对象相加的顺序很重要。举例说明:

>>> policy100 = policy.compat32.clone(max_line_length=100)
>>> policy80 = policy.compat32.clone(max_line_length=80)
>>> apolicy = policy100 + policy80
>>> apolicy.max_line_length
80
>>> apolicy = policy80 + policy100
>>> apolicy.max_line_length
100
class email.policy.Policy(**kw)

这是所有策略类的抽象基类。它为一些简单的方法提供了默认实现,以及不可变性属性的实现、clone() 方法和构造函数的语义。

策略类的构造函数可以接受各种关键字参数。可以指定的参数是这个类上的任何非方法属性,以及具体类上的任何额外的非方法属性。在构造函数中指定的值将覆盖相应属性的默认值。

该类定义了以下属性,因此以下属性的值可以在任何策略类的构造函数中传递:

max_line_length

序列化输出中任何行的最大长度,不包括行尾字符。默认为 78,依据 RFC 5322。值为 0None 表示完全不进行换行。

linesep

用于在序列化输出中终止行的字符串。默认是 \n,因为这是 Python 内部使用的行尾规则,尽管 RFCs 要求使用 \r\n

cte_type

控制可能或必须使用的内容传输编码(Content Transfer Encodings)的类型。可能的值是:

7bit

所有数据必须是“7 位安全的”(仅 ASCII)。这意味着在必要时,数据将使用 quoted-printable 或 base64 编码进行编码。

8bit

数据不限于 7 位安全。标头中的数据仍然需要是仅 ASCII 的,因此会被编码(有关例外情况,请参见下面的 fold_binary()utf8),但正文部分可以使用 8bit CTE。

cte_type 值为 8bit 仅适用于 BytesGenerator,不适用于 Generator,因为字符串不能包含二进制数据。如果一个 Generator 在指定 cte_type=8bit 的策略下运行,它的行为将如同 cte_type7bit 一样。

raise_on_defect

如果为 True,任何遇到的缺陷都会被作为错误引发。如果为 False(默认值),缺陷将被传递给 register_defect() 方法。

mangle_from_

如果为 True,正文中以 *“From ”* 开头的行会通过在它们前面加上一个 > 来进行转义。此参数在消息由生成器序列化时使用。默认值:False

在 3.5 版本加入。

message_factory

用于构造新空消息对象的工厂函数。解析器在构建消息时使用。默认为 None,在这种情况下使用 Message

在 3.6 版本加入。

verify_generated_headers

如果为 True(默认值),生成器将引发 HeaderWriteError,而不是写入一个折叠或分隔不当的标头,这样的标头可能会被解析为多个标头或与相邻数据连接。这类标头可能由自定义标头类或 email 模块中的错误生成。

由于它是一项安全特性,即使在 Compat32 策略中,它也默认为 True。为了实现向后兼容但行为不安全的模式,必须将其显式设置为 False

在 3.13 版本加入。

以下 Policy 方法旨在由使用 email 库的代码调用,以创建具有自定义设置的策略实例:

clone(**kw)

返回一个新的 Policy 实例,其属性值与当前实例相同,除非这些属性通过关键字参数被赋予了新值。

其余的 Policy 方法由 email 包的代码调用,不应由使用 email 包的应用程序调用。自定义策略必须实现所有这些方法。

handle_defect(obj, defect)

处理在 obj 上发现的 defect。当 email 包调用此方法时,defect 将始终是 MessageDefect 的子类。

默认实现会检查 raise_on_defect 标志。如果为 Truedefect 将作为异常被引发。如果为 False(默认值),objdefect 将被传递给 register_defect()

register_defect(obj, defect)

obj 上注册一个 defect。在 email 包中,defect 将始终是 MessageDefect 的子类。

默认实现会调用 objdefects 属性的 append 方法。当 email 包调用 handle_defect 时,obj 通常会有一个具有 append 方法的 defects 属性。与 email 包一起使用的自定义对象类型(例如,自定义 Message 对象)也应提供这样的属性,否则解析消息中的缺陷将引发意外错误。

header_max_count(name)

返回名为 name 的标头所允许的最大数量。

在向 EmailMessageMessage 对象添加标头时调用。如果返回的值不是 0None,并且已经存在名为 name 的标头数量大于或等于返回的值,则会引发 ValueError

由于 Message.__setitem__ 的默认行为是将值附加到标头列表中,因此很容易在不知不觉中创建重复的标头。此方法允许限制某些标头可以以编程方式添加到 Message 对象中的实例数量。(解析器不会遵守此限制,它将忠实地生成消息中存在的所有标头。)

默认实现对所有标头名称返回 None

header_source_parse(sourcelines)

email 包使用一个字符串列表调用此方法,每个字符串都以在被解析的源中找到的行分隔符结尾。第一行包括字段标头名称和分隔符。源中的所有空白都将被保留。该方法应返回 (name, value) 元组,该元组将存储在 Message 中以表示解析后的标头。

如果实现希望保持与现有 email 包策略的兼容性,name 应该是保留大小写的名称(直到“:”分隔符之前的所有字符),而 value 应该是展开后的值(所有行分隔符被移除,但空白保持不变),并去除前导空白。

sourcelines 可能包含 surrogateescaped 编码的二进制数据。

没有默认实现。

header_store_parse(name, value)

当应用程序以编程方式修改 Message(而不是由解析器创建的 Message)时,email 包会使用应用程序提供的名称和值调用此方法。该方法应返回 (name, value) 元组,该元组将存储在 Message 中以表示该标头。

如果实现希望保持与现有 email 包策略的兼容性,namevalue 应该是不会改变传入参数内容的字符串或字符串子类。

没有默认实现。

header_fetch_parse(name, value)

当应用程序请求某个标头时,email 包会使用当前存储在 Message 中的 namevalue 调用此方法,而该方法返回的任何内容都将作为被检索标头的值返回给应用程序。请注意,Message 中可能存储了多个同名的标头;该方法被传递的是将要返回给应用程序的特定标头的名称和值。

value 可能包含 surrogateescaped 编码的二进制数据。方法返回的值中不应包含 surrogateescaped 编码的二进制数据。

没有默认实现。

fold(name, value)

email 包使用当前存储在 Message 中给定标头的 namevalue 调用此方法。该方法应返回一个字符串,表示该标头通过组合 namevalue 并在适当位置插入 linesep 字符而(根据策略设置)正确“折叠”的结果。有关折叠电子邮件标头的规则的讨论,请参阅 RFC 5322

value 可能包含 surrogateescaped 编码的二进制数据。方法返回的字符串中不应包含 surrogateescaped 编码的二进制数据。

fold_binary(name, value)

fold() 相同,只是返回的值应为字节对象而不是字符串。

value 可能包含 surrogateescaped 编码的二进制数据。这些数据可以在返回的字节对象中转换回二进制数据。

class email.policy.EmailPolicy(**kw)

这个具体的 Policy 提供了旨在完全符合当前电子邮件 RFC 的行为。这些 RFC 包括(但不限于)RFC 5322RFC 2047 以及当前的 MIME RFC。

此策略添加了新的标头解析和折叠算法。标头不再是简单的字符串,而是 str 的子类,其属性取决于字段类型。解析和折叠算法完全实现了 RFC 2047RFC 5322

message_factory 属性的默认值是 EmailMessage

除了上面列出的适用于所有策略的可设置属性外,此策略还添加了以下附加属性:

在 3.6 版本加入: [1]

utf8

如果为 False,则遵循 RFC 5322,通过将非 ASCII 字符编码为“编码词”来支持标头中的这些字符。如果为 True,则遵循 RFC 6532 并对标头使用 utf-8 编码。以此方式格式化的消息可以传递给支持 SMTPUTF8 扩展(RFC 6531)的 SMTP 服务器。

refold_source

如果 Message 对象中标头的值源自 parser(而不是由程序设置),此属性指示生成器在将消息转换回序列化形式时是否应重新折叠该值。可能的值为:

none

所有源值都使用原始的折叠方式

long

任何行长于 max_line_length 的源值都将被重新折叠

all

所有值都会被重新折叠。

默认值为 long

header_factory

一个可调用对象,接受两个参数 namevalue,其中 name 是标头字段名,value 是未折叠的标头字段值,并返回一个表示该标头的字符串子类。提供了一个默认的 header_factory(见 headerregistry),它支持对各种地址和日期 RFC 5322 标头字段类型以及主要的 MIME 标头字段类型进行自定义解析。将来会增加对其他自定义解析的支持。

content_manager

一个至少有两个方法的对象:get_content 和 set_content。当调用 EmailMessage 对象的 get_content()set_content() 方法时,它会调用此对象的相应方法,将消息对象作为其第一个参数,并将传递给它的任何参数或关键字作为附加参数传递。默认情况下,content_manager 被设置为 raw_data_manager

在 3.4 版本加入。

该类提供了 Policy 抽象方法的以下具体实现:

header_max_count(name)

返回用于表示给定名称标头的专用类的 max_count 属性的值。

header_source_parse(sourcelines)

名称被解析为直到“:”之前的所有内容并原样返回。值是通过去除第一行剩余部分的前导空格,将所有后续行连接在一起,并去除任何尾随的回车或换行符来确定的。

header_store_parse(name, value)

名称原样返回。如果输入值具有一个 name 属性且其与 name 忽略大小写匹配,则该值原样返回。否则,namevalue 会被传递给 header_factory,返回的结果标头对象作为值。在这种情况下,如果输入值包含 CR 或 LF 字符,则会引发 ValueError

header_fetch_parse(name, value)

如果值有一个 name 属性,则它会被原样返回。否则,name 和移除了任何 CR 或 LF 字符的 value 将被传递给 header_factory,并返回生成的标头对象。任何代理转义的字节都会变成 unicode 的未知字符符号。

fold(name, value)

标头折叠由 refold_source 策略设置控制。一个值当且仅当它没有 name 属性时,才被认为是“源值”(拥有 name 属性意味着它是某种标头对象)。如果根据策略需要重新折叠一个源值,它会被转换成一个标头对象,方法是将 name 和去除了任何 CR 和 LF 字符的 value 传递给 header_factory。标头对象的折叠是通过调用其 fold 方法并传入当前策略来完成的。

源值使用 splitlines() 分割成行。如果该值不需重新折叠,则这些行将使用策略中的 linesep 重新连接并返回。例外情况是包含非 ASCII 二进制数据的行。在这种情况下,无论 refold_source 设置如何,该值都会被重新折叠,这会导致二进制数据使用 unknown-8bit 字符集进行 CTE 编码。

fold_binary(name, value)

如果 cte_type7bit,则与 fold() 相同,只是返回的值是字节。

如果 cte_type8bit,非 ASCII 二进制数据将被转换回字节。含有二进制数据的标头不会被重新折叠,无论 refold_header 设置如何,因为无法知道二进制数据是由单字节字符还是多字节字符组成。

以下 EmailPolicy 实例为特定应用领域提供了合适的默认值。请注意,将来这些实例的行为(特别是 HTTP 实例)可能会被调整,以更紧密地符合其相关领域的 RFC。

email.policy.default

一个 EmailPolicy 的实例,所有默认值均未改变。此策略使用标准的 Python \n 换行符,而不是 RFC 规范的 \r\n

email.policy.SMTP

适用于按照电子邮件 RFCs 规范序列化消息。与 default 类似,但 linesep 设置为 \r\n,这符合 RFC 规范。

email.policy.SMTPUTF8

SMTP 相同,但 utf8True。适用于将消息序列化到消息存储中,而不在标头中使用编码词。仅当发件人或收件人地址含有非 ASCII 字符时才应用于 SMTP 传输(smtplib.SMTP.send_message() 方法会自动处理这种情况)。

email.policy.HTTP

适用于序列化用于 HTTP 流量的标头。与 SMTP 类似,但 max_line_length 设置为 None(无限制)。

email.policy.strict

便利实例。与 default 相同,但 raise_on_defect 设置为 True。这允许通过编写以下代码使任何策略变得严格:

somepolicy + policy.strict

对于所有这些 EmailPolicies,email 包的有效 API 与 Python 3.2 API 相比有以下变化:

  • Message 上设置一个标头会导致该标头被解析并创建一个标头对象。

  • Message 获取一个标头值会导致该标头被解析,并创建并返回一个标头对象。

  • 任何标头对象,或任何由于策略设置而被重新折叠的标头,都将使用一个完全实现了 RFC 折叠算法的算法进行折叠,包括知道在何处需要和允许使用编码词。

从应用程序的角度来看,这意味着通过 EmailMessage 获取的任何标头都是一个带有额外属性的标头对象,其字符串值是标头的完全解码的 unicode 值。同样,可以使用 unicode 字符串为标头赋新值或创建新标头,策略将负责将 unicode 字符串转换为正确的 RFC 编码形式。

标头对象及其属性在 headerregistry 中有描述。

class email.policy.Compat32(**kw)

这个具体的 Policy 是向后兼容策略。它复制了 email 包在 Python 3.2 中的行为。policy 模块也定义了这个类的一个实例 compat32,它被用作默认策略。因此,email 包的默认行为是保持与 Python 3.2 的兼容性。

以下属性的值与 Policy 的默认值不同:

mangle_from_

默认值为 True

该类提供了 Policy 抽象方法的以下具体实现:

header_source_parse(sourcelines)

名称被解析为直到“:”之前的所有内容并原样返回。值是通过去除第一行剩余部分的前导空格,将所有后续行连接在一起,并去除任何尾随的回车或换行符来确定的。

header_store_parse(name, value)

名称和值将不加修改地返回。

header_fetch_parse(name, value)

如果值包含二进制数据,它将被使用 unknown-8bit 字符集转换为 Header 对象。否则,它将不加修改地返回。

fold(name, value)

标头使用 Header 折叠算法进行折叠,该算法保留值中现有的换行符,并将每个结果行包装到 max_line_length。非 ASCII 二进制数据使用 unknown-8bit 字符集进行 CTE 编码。

fold_binary(name, value)

标头使用 Header 折叠算法进行折叠,该算法保留值中现有的换行符,并将每个结果行包装到 max_line_length。如果 cte_type7bit,非 ASCII 二进制数据将使用 unknown-8bit 字符集进行 CTE 编码。否则,将使用原始的源标头,包括其现有的换行符和它可能包含的任何(RFC 无效的)二进制数据。

email.policy.compat32

Compat32 的一个实例,提供了与 Python 3.2 中 email 包行为的向后兼容性。

脚注