email.headerregistry: 自定义标头对象

源代码: Lib/email/headerregistry.py


在 3.6 版本加入: [1]

标头由 str 的自定义子类来表示。用于表示给定标头的特定类由创建标头时生效的 policyheader_factory 决定。本节文档介绍了 email 包为处理符合 RFC 5322 的电子邮件消息而实现的特定 header_factory,它不仅为各种标头类型提供了自定义标头对象,还为应用程序提供了一种扩展机制,以添加自己的自定义标头类型。

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

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

class email.headerregistry.BaseHeader(name, value)

namevalue 是通过调用 header_factory 传递给 BaseHeader 的。任何标头对象的字符串值都是完全解码为 Unicode 的 value

这个基类定义了以下只读属性:

name

标头的名称(字段中“:”之前的部分)。这与在 header_factory 调用中为 name 传递的值完全相同;也就是说,大小写被保留。

defects

一个 HeaderDefect 实例的元组,报告在解析期间发现的任何 RFC 合规性问题。email 包试图完整地检测合规性问题。有关可能报告的缺陷类型的讨论,请参见 errors 模块。

max_count

具有相同 name 的此类标头的最大数量。值为 None 意味着无限制。BaseHeader 对此属性的值为 None;专门的标头类会根据需要覆盖此值。

BaseHeader 还提供了以下方法,该方法由 email 库代码调用,通常不应由应用程序调用:

fold(*, policy)

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

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

parse(string, kwds)

kwds 是一个包含一个预初始化键 defects 的字典。defects 是一个空列表。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 字符。当一个包含编码词的 value 传递给构造函数时,UnstructuredHeader 解析器会根据 RFC 2047 的非结构化文本规则将这些编码词转换为 Unicode。解析器使用启发式方法尝试解码某些不合规的编码词。在这种情况下会注册缺陷,同时也会为编码词或未编码文本中的无效字符等问题注册缺陷。

此标头类型不提供其他属性。

class email.headerregistry.DateHeader

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

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

datetime

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

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

email.utils.format_datetime(self.datetime)

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

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

由于这是一个 naive datetime,它将被解释为 UTC 时间戳,并且结果值将具有 -0000 的时区。更有用的是使用 utils 模块中的 localtime() 函数:

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

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

class email.headerregistry.AddressHeader

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

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

groups

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

addresses

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

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

可以使用 AddressGroup 对象的任意组合列表来设置地址标头的值。其 display_nameNoneGroup 对象将被解释为单个地址,这允许通过使用从源标头的 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。如果为 Noneaddresses 中只有一个 Address,则 Group 表示一个不在组中的单个地址。

addresses

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

__str__()

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

脚注