email.headerregistry: 自定义报头对象

源代码: Lib/email/headerregistry.py


版本 3.6 中新增: [1]

报头由 str 的自定义子类表示。用于表示给定报头的特定类由 header_factory 决定,该工厂在创建报头时生效的 policy 中定义。本节介绍电子邮件包为处理 RFC 5322 兼容电子邮件消息而实现的特定 header_factory,它不仅为各种报头类型提供自定义报头对象,还提供了一种扩展机制,允许应用程序添加自己的自定义报头类型。

当使用从 EmailPolicy 派生的任何策略对象时,所有报头都由 HeaderRegistry 生成,并且以 BaseHeader 作为它们的最后一个基类。每个报头类都有一个额外的基类,该基类由报头的类型决定。例如,许多报头以 UnstructuredHeader 类作为它们的另一个基类。报头的专用第二个类由报头的名称决定,使用存储在 HeaderRegistry 中的查找表。所有这些对于典型的应用程序程序都是透明管理的,但提供了用于修改默认行为的接口,供更复杂的应用程序使用。

以下各节首先介绍报头基类及其属性,然后介绍修改 HeaderRegistry 行为的 API,最后介绍用于表示从结构化报头解析的数据的支持类。

class email.headerregistry.BaseHeader(name, value)

namevalueheader_factory 调用传递给 BaseHeader。任何报头对象的字符串值都是完全解码为 Unicode 的 value

此基类定义了以下只读属性

name

报头的名称(字段中冒号之前的部分)。这与在 header_factory 调用中为 name 传递的值完全相同;也就是说,大小写保持不变。

defects

一个包含 HeaderDefect 实例的元组,报告在解析过程中发现的任何 RFC 兼容性问题。电子邮件包尽力全面检测兼容性问题。有关可能报告的缺陷类型的讨论,请参见 errors 模块。

max_count

具有相同 name 的此类型报头的最大数量。值为 None 表示无限制。此属性的 BaseHeader 值为 None;预计专用报头类将根据需要覆盖此值。

BaseHeader 还提供了以下方法,该方法由电子邮件库代码调用,通常不应该由应用程序程序调用

fold(*, policy)

返回一个字符串,其中包含 linesep 字符,这些字符是根据 policy 正确折叠报头所需的。cte_type8bit 将被视为 7bit,因为报头可能不包含任意二进制数据。如果 utf8False,则非 ASCII 数据将被 RFC 2047 编码。

BaseHeader 本身不能用于创建报头对象。它定义了一种协议,每个专用报头都与该协议协作以生成报头对象。具体来说,BaseHeader 要求专用类提供一个名为 parseclassmethod()。此方法按如下方式调用

parse(string, kwds)

kwds 是一个字典,其中包含一个预初始化的键 defectsdefects 是一个空列表。parse 方法应该将任何检测到的缺陷追加到此列表中。返回时,kwds 字典必须包含至少 decodeddefects 键的值。 decoded 应该是报头的字符串值(即完全解码为 Unicode 的报头值)。parse 方法应该假设 string 可能包含内容传输编码部分,但应该正确处理所有有效的 Unicode 字符,以便它可以解析未编码的报头值。

BaseHeader__new__ 方法会创建头实例,并调用其 init 方法。如果专用类希望设置除 BaseHeader 本身提供的属性之外的附加属性,则它只需要提供一个 init 方法。这样的 init 方法应该如下所示

def init(self, /, *args, **kw):
    self._myattr = kw.pop('myattr')
    super().init(*args, **kw)

也就是说,专用类在 kwds 字典中添加的任何额外内容都应该被移除和处理,并将 kw(和 args)的剩余内容传递给 BaseHeaderinit 方法。

class email.headerregistry.UnstructuredHeader

“非结构化”头是 RFC 5322 中默认类型的头。任何没有指定语法的头都被视为非结构化的。非结构化头的典型示例是 Subject 头。

RFC 5322 中,非结构化头是 ASCII 字符集中的任意文本运行。但是,RFC 2047 具有 RFC 5322 兼容机制,用于将非 ASCII 文本编码为头值内的 ASCII 字符。当包含编码词的传递给构造函数时,UnstructuredHeader 解析器会将这些编码词转换为 Unicode,遵循 RFC 2047 中关于非结构化文本的规则。解析器使用启发式方法尝试解码某些不符合规范的编码词。在这些情况下,会注册缺陷,以及诸如编码词或非编码文本中包含无效字符等问题的缺陷。

此头类型不提供任何附加属性。

class email.headerregistry.DateHeader

RFC 5322 指定了电子邮件头中日期的非常具体的格式。 DateHeader 解析器识别该日期格式,以及识别有时在“野外”中发现的一些变体形式。

此头类型提供以下附加属性

datetime

如果头值可以被识别为某种形式的有效日期,则此属性将包含一个 datetime 实例,表示该日期。如果输入日期的时区被指定为 -0000(表示它在 UTC 中,但没有包含有关源时区的信息),则 datetime 将是一个朴素的 datetime。如果找到特定的时区偏移量(包括 +0000),则 datetime 将包含一个使用 datetime.timezone 记录时区偏移量的感知 datetime

头的 decoded 值是通过根据 RFC 5322 规则格式化 datetime 来确定的;也就是说,它被设置为

email.utils.format_datetime(self.datetime)

在创建 DateHeader 时,value 可以是 datetime 实例。这意味着,例如,以下代码是有效的,并且按预期执行

msg['Date'] = datetime(2011, 7, 15, 21)

因为这是一个朴素的 datetime,所以它将被解释为 UTC 时间戳,并且结果值将具有 -0000 的时区。更有用的是使用 localtime() 函数,该函数来自 utils 模块

msg['Date'] = utils.localtime()

此示例将日期头设置为当前时间和日期,使用当前时区偏移量。

class email.headerregistry.AddressHeader

地址头是最复杂的结构化头类型之一。 AddressHeader 类为任何地址头提供了一个通用接口。

此头类型提供以下附加属性

groups

一个 Group 对象元组,用于编码头值中找到的地址和组。不在组中的地址在此列表中表示为单个地址 Groups,其 display_nameNone

addresses

一个 Address 对象元组,用于编码头值中的所有单个地址。如果头值包含任何组,则来自组的单个地址将包含在组在值中出现的位置的列表中(也就是说,地址列表被“扁平化”为一维列表)。

头的 decoded 值将具有所有解码为 Unicode 的编码词。 idna 编码的域名也被解码为 Unicode。 decoded 值是通过使用 ', ' 连接 groups 属性的元素的 str 值来设置的。

地址头可以使用任何组合的 AddressGroup 对象来设置其值。 Group 对象的 display_nameNone 将被解释为单个地址,这允许使用从源地址头的 groups 属性获得的列表来完整地复制地址列表。

class email.headerregistry.SingleAddressHeader

AddressHeader 的子类,添加了一个额外的属性

address

由头值编码的单个地址。如果头值实际上包含多个地址(这将违反默认 policy 下的 RFC),访问此属性将导致 ValueError

上面许多类还具有 Unique 变体(例如,UniqueUnstructuredHeader)。唯一的区别是,在 Unique 变体中,max_count 设置为 1。

class email.headerregistry.MIMEVersionHeader

MIME-Version 头实际上只有一个有效值,即 1.0。为了将来证明,此头类支持其他有效版本号。如果版本号根据 RFC 2045 具有有效值,则头对象将对以下属性具有非 None

version

版本号作为字符串,已删除所有空格和/或注释。

major

主版本号作为整数

minor

次版本号作为整数

class email.headerregistry.ParameterizedMIMEHeader

MIME 头都以前缀 ‘Content-’ 开头。每个特定头都有一个特定值,在该头的类下描述。一些头还可以接受补充参数列表,这些参数具有通用格式。此类用作所有接受参数的 MIME 头的基类。

params

一个字典,将参数名称映射到参数值。

class email.headerregistry.ContentTypeHeader

一个 ParameterizedMIMEHeader 类,用于处理 Content-Type 头。

content_type

内容类型字符串,格式为 maintype/subtype

maintype
subtype
class email.headerregistry.ContentDispositionHeader

一个 ParameterizedMIMEHeader 类,用于处理 Content-Disposition 头。

content_disposition

inlineattachment 是唯一常用的有效值。

class email.headerregistry.ContentTransferEncoding

处理 Content-Transfer-Encoding 头。

cte

有效值为 7bit8bitbase64quoted-printable。有关更多信息,请参阅 RFC 2045

class email.headerregistry.HeaderRegistry(base_class=BaseHeader, default_class=UnstructuredHeader, use_default_map=True)

这是 EmailPolicy 默认使用的工厂。 HeaderRegistry 使用 base_class 和从其持有的注册表中检索的专用类动态构建用于创建头实例的类。当给定的头名称未出现在注册表中时,default_class 指定的类将用作专用类。当 use_default_mapTrue(默认值)时,头名称到类的标准映射将在初始化期间复制到注册表中。base_class 始终是生成的类的 __bases__ 列表中的最后一个类。

默认映射为

subject:

UniqueUnstructuredHeader

date:

UniqueDateHeader

resent-date:

DateHeader

orig-date:

UniqueDateHeader

sender:

UniqueSingleAddressHeader

resent-sender:

SingleAddressHeader

to:

UniqueAddressHeader

resent-to:

AddressHeader

cc:

UniqueAddressHeader

resent-cc:

AddressHeader

bcc:

UniqueAddressHeader

resent-bcc:

AddressHeader

from:

UniqueAddressHeader

resent-from:

AddressHeader

reply-to:

UniqueAddressHeader

mime-version:

MIMEVersionHeader

content-type:

ContentTypeHeader

content-disposition:

ContentDispositionHeader

content-transfer-encoding:

ContentTransferEncodingHeader

message-id:

MessageIDHeader

HeaderRegistry 具有以下方法

map_to_type(self, name, cls)

name 是要映射的头名称。它将在注册表中转换为小写。cls 是要使用的专用类,它与 base_class 一起用于创建用于实例化与 name 匹配的头的类。

__getitem__(name)

构造并返回一个类来处理创建 name 头。

__call__(name, value)

从注册表中检索与 name 关联的专用头(如果 name 未出现在注册表中,则使用 default_class),并将其与 base_class 组合以生成一个类,调用构造的类的构造函数,将相同的参数列表传递给它,最后返回由此创建的类实例。

以下类是用于表示从结构化头解析的数据的类,通常可以由应用程序程序使用来构造结构化值以分配给特定头。

class email.headerregistry.Address(display_name='', username='', domain='', addr_spec=None)

用于表示电子邮件地址的类。地址的一般形式为

[display_name] <username@domain>

username@domain

其中每个部分必须符合 RFC 5322 中规定的特定语法规则。

为了方便起见,可以指定 addr_spec 而不是 usernamedomain,在这种情况下,usernamedomain 将从 addr_spec 中解析。addr_spec 必须是正确的 RFC 引号字符串;如果不是,Address 将引发错误。允许使用 Unicode 字符,并在序列化时进行适当编码。但是,根据 RFC,地址的用户名部分不允许使用 Unicode。

display_name

地址的显示名称部分(如果有),已删除所有引号。如果地址没有显示名称,则此属性将为空字符串。

username

地址的 username 部分,已删除所有引号。

domain

地址的 domain 部分。

addr_spec

地址的 username@domain 部分,已正确引用以用作裸地址(上面显示的第二种形式)。此属性不可变。

__str__()

对象的 str 值是根据 RFC 5322 规则引用的地址,但没有对任何非 ASCII 字符进行内容传输编码。

为了支持 SMTP (RFC 5321),Address 处理一个特殊情况:如果 usernamedomain 都是空字符串(或 None),则 Address 的字符串值为 <>

class email.headerregistry.Group(display_name=None, addresses=None)

用于表示地址组的类。地址组的一般形式为

display_name: [address-list];

为了方便处理由组和单个地址混合组成的地址列表,Group 也可以用于表示不是组一部分的单个地址,方法是将 display_name 设置为 None 并提供单个地址的列表作为 addresses

display_name

组的 display_name。如果它为 None 并且 addresses 中只有一个 Address,则 Group 表示不在组中的单个地址。

addresses

一个可能为空的 Address 对象元组,表示组中的地址。

__str__()

Groupstr 值是根据 RFC 5322 格式化的,但没有对任何非 ASCII 字符进行内容传输编码。如果 display_name 为 none 并且 addresses 列表中只有一个 Address,则 str 值将与该单个 Addressstr 相同。

脚注