email.headerregistry: 自定义标头对象

源代码: Lib/email/headerregistry.py


3.6 版本新增: [1]

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

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

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

class email.headerregistry.BaseHeader(name, value)

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

此基类定义以下只读属性

name

标头的名称(字段中 ‘:’ 之前的部分)。这正是为 nameheader_factory 调用中传递的值;也就是说,保留了大小写。

defects

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

max_count

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

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

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)的剩余内容传递给 BaseHeader init 方法。

class email.headerregistry.UnstructuredHeader

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

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

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

class email.headerregistry.DateHeader

RFC 5322 为电子邮件头部中的日期指定了非常具体的格式。DateHeader 解析器可以识别该日期格式,并识别有时在“实际应用中”发现的许多变体形式。

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

datetime

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

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

email.utils.format_datetime(self.datetime)

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

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

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

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

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

class email.headerregistry.AddressHeader

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

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

groups

一个 Group 对象的元组,用于编码头部值中找到的地址和组。不属于组的地址在此列表中表示为单地址 Groups,其 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,在这种情况下,将从 addr_spec 解析 usernamedomainaddr_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 值相同。

脚注