19.1.6. "email.headerregistry": 自定义标头对象
**********************************************

**源代码:** Lib/email/headerregistry.py

======================================================================

3.6 新版功能: [1]

标头是由 "str" 的自定义子类来表示的。 用于表示给定标头的特定类则由创建
标头时生效的 "policy" 的 "header_factory" 确定。 这一节记录了 email 包
为处理兼容 **RFC 5322** 的电子邮件消息所实现的特定 "header_factory"，
它不仅为各种标头类型提供了自定义的标头对象，还为应用程序提供了添加其自
定义标头类型的扩展机制。

当使用派生自 "EmailPolicy" 的任何策略对象时，所有标头都通过
"HeaderRegistry" 产生并且以 "BaseHeader" 作为其最后一个基类。 每个标头
类都有一个由该标头类型确定的附加基类。 例如，许多标头都以
"UnstructuredHeader" 类作为其另一个基类。 一个标头专用的第二个类是由标
头名称使用存储在 "HeaderRegistry" 中的查找表来确定的。 所有这些都针对
典型应用程序进行透明的管理，但也为修改默认行为提供了接口，以便由更复杂
的应用使用。

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

class email.headerregistry.BaseHeader(name, value)

   *name* 和 *value* 会从 "header_factory" 调用传递给 "BaseHeader"。
   任何标头对象的字符串值都是完成解码为 unicode 的 *value*。

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

   name

      标头的名称（字段在 ':' 之前的部分）。 这就是 *name* 的
      "header_factory" 调用所传递的值；也就是说会保持大小写形式。

   defects

      一个包含 "HeaderDefect" 实例的元组，这些实例报告了在解析期间发现
      的任何 RFC 合规性问题。 email 包会尝试尽可能地检测合规性问题。
      请参阅 "errors" 模块了解可能被报告的缺陷类型的相关讨论。

   max_count

      此类型标头可具有相同 "name" 的最大数量。 "None" 值表示无限制。
      此属性的 "BaseHeader" 值为 "None"；专用的标头类预期将根据需要重
      载这个值。

   "BaseHeader" 还提供了以下方法，它由 email 库代码调用，通常不应当由
   应用程序来调用。

   fold(*, policy)

      返回一个字符串，其中包含用来根据 *policy* 正确地折叠标头的
      "linesep" 字符。  "cte_type" 为 "8bit" 时将被作为 "7bit" 来处理
      ，因为标头不能包含任意二进制数据。 如果 "utf8" 为 "False"，则非
      ASCII 数据将根据 **RFC 2047** 来编码。

   "BaseHeader" 本身不能被用于创建标头对象。 它定义了一个与每个专用标
   头相配合的协议以便生成标头对象。 具体来说，"BaseHeader" 要求专用类
   提供一个名为 "parse" 的 "classmethod()"。 此方法的调用形式如下:

      parse(string, kwds)

   "kwds" 是包含了一个预初始化键 "defects" 的字典。 "defects" 是一个空
   列表。 parse 方法应当将任何已检测到的缺陷添加到此列表中。 在返回时
   ，"kwds" 字典 *必须* 至少包含 "decoded" 和 "defects" 等键的值。
   "decoded" 应当是标头的字符串值（即完全解码为 unicode 的标头值）。
   parse 方法应当假定 *string* 可能包含 content-transfer-encoded 部分
   ，但也应当正确地处理全部有效的 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 字符。 当包含已编码字的 *value* 被传递
   给构造器时，"UnstructuredHeader" 解析器会按照非结构化文本的 **RFC
   2047** 规则将此类已编码字转换为 unicode。 解析器会使用启发式机制来
   尝试解码一些不合规的已编码字。 在此种情况下各类缺陷，例如已编码字或
   未编码文本中的无效字符问题等缺陷将会被注册。

   此标头类型未提供附加属性。

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"。 使用来自 "utils" 模块的 "localtime()" 函数会更有用
   :

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

   这个例子将日期标头设为使用当前时区时差值的当前时间和日期。

class email.headerregistry.AddressHeader

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

   这个标头类型提供了以下附加属性。

   groups

      编码了在标头值中找到的地址和分组的 "Group" 对象的元组。 非分组成
      员的地址在此列表中表示为 "display_name" 为 "None" 的单地址
      "Groups"。

   addresses

      编码了来自标头值的所有单独地址的 "Address" 对象的元组。 如果标头
      值包含任何分组，则来自分组的单个地址将包含在该分组出现在值中的点
      上列出（也就是说，地址列表会被“展平”为一维列表）。

   标头的 "decoded" 值将为所有已编码字解码为 unicode 的结果。   "idna"
   编码的域名也将被解码为 unicode。 "decoded" 值是通过对 "groups" 属性
   的元素的 "str" 值使用 "', '" 进行 "join" 来设置的。

   可以使用 "Address" 与 "Group" 对象的任意组合的列表来设置一个地址标
   头的值。 "display_name" 为 "None" 的 "Group" 对象将被解读为单独地址
   ，这允许一个地址列表可以附带通过使用从源标头的 "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

   处理 *Content-Type* 标头的  "ParameterizedMIMEHeader" 类。

   content_type

      "maintype/subtype" 形式的内容类型字符串。

   maintype

   subtype

class email.headerregistry.ContentDispositionHeader

   处理 *Content-Disposition* 标头的 "ParameterizedMIMEHeader" 类。

   content-disposition

      "inline" 和 "attachment" 是仅有的常用有效值。

class email.headerregistry.ContentTransferEncoding

   处理 *Content-Transfer-Encoding* 标头。

   cte

      可用的有效值为 "7bit", "8bit", "base64" 和 "quoted-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_map* 为
   "True" (默认值) 时，则会在初始化期间把将标头名称与类的标准映射拷贝
   到注册表中。  *base_class* 始终会是所生成类的 "__bases__" 列表中的
   最后一个类。

   默认的映射有:

      subject:
         UniqueUnstructuredHeader

      date:
         UniqueDateHeader

      resent-date:
         DateHeader

      orig-date:
         UniqueDateHeader

      sender:
         UniqueSingleAddressHeader

      resent-sender:
         SingleAddressHeader

      到:
         UniqueAddressHeader

      resent-to:
         AddressHeader

      cc:
         UniqueAddressHeader

      resent-cc:
         AddressHeader

      从:
         UniqueAddressHeader

      resent-from:
         AddressHeader

      reply-to:
         UniqueAddressHeader

   "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* 来替代 *username* 和 *domain*，在
   此情况下 *username* 和 *domain* 将从 *addr_spec* 中解析。
   *addr_spec* 应当是一个正确地引用了 RFC 的字符串；如果它不是
   "Address" 则将引发错误。 Unicode 字符也允许使用并将在序列化时被正确
   地编码。 但是，根据 RFC，地址的 username 部分 *不允许* 有 unicode。

   display_name

      地址的显示名称部分（如果有的话）并去除所有引用项。 如果地址没有
      显示名称，则此属性将为空字符串。

   username

      地址的 "username" 部分，去除所有引用项。

   domain

      地址的 "domain" 部分。

   addr_spec

      地址的 "username@domain" 部分，经过正确引用处理以作为纯地址使用
      （上面显示的第二种形式）。 此属性不可变。

   __str__()

      对象的 "str" 值是根据 **RFC 5322** 规则进行引用处理的地址，但不
      带任何非 ASCII 字符的 Content Transfer Encoding。

   为了支持 SMTP (**RFC 5321**)，"Address" 会处理一种特殊情况：如果
   "username" 和 "domain" 均为空字符串 (或为 "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" 并且恰好有一个 "Address"
      在 "addresses" 中，则 "Group" 表示一个不在某个组中的单独地址。

   addresses

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

   __str__()

      "Group" 的 "str" 值会根据 **RFC 5322** 进行格式化，但不带任何非
      ASCII 字符的 Content Transfer Encoding。 如果 "display_name" 为
      空值且只有一个单独 "Address" 在 "addresses" 列表中，则 "str" 值
      将与该单独 "Address" 的 "str" 相同。

-[ 备注 ]-

[1] 最初在 3.3 中作为 *暂定模块* 添加
