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

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

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

Added in version 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

   An "unstructured" header is the default type of header in **RFC
   5322**. Any header that does not have a specified syntax is treated
   as unstructured.  The classic example of an unstructured header is
   the *Subject* header.

   在 **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" 值使用 "', '" 进行 合并 来设置的。

   可以使用 "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

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

      在 3.15 版本发生变更: It is now a "frozendict" instead of a
      "types.MappingProxyType".

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.ContentTransferEncodingHeader

   处理 *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

      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* 来替代 *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 中作为 *暂定模块* 添加
