# `email.generator`: 生成 MIME 文档

源代码: Lib/email/generator.py

最常见的任务之一是生成由消息对象结构体表示的电子邮件消息的展平（序列化）版本。如果你想通过 `smtplib.SMTP.sendmail()` 来发送你的消息或是将消息打印到控制台就会需要这样做。接受一个消息对象结构体并生成其序列化表示就是这些生成器类的工作。

与 `email.parser` 模块一样，你并不会受限于已捆绑生成器的功能；你可以自己从头写一个。 不过，已捆绑生成器知道如何以符合标准的方式来生成大多数电子邮件，应该能够很好地处理 MIME 和非 MIME 电子邮件消息，并且被设计为面向字节的解析和生成操作是互逆的，它假定两者都使用同样的非转换型 `policy`。也就是说，通过 `BytesParser` 类来解析序列化字节流然后再使用 `BytesGenerator` 来重新生成序列化字节流应当得到与输入相同的结果 [1]。 （而另一方面，在由程序所构造的 `EmailMessage` 上使用生成器可能导致对默认填入的 `EmailMessage` 对象的改变。）

可以使用 `Generator` 类将消息扁平化为文本（而非二进制数据）的序列化表示形式，但是由于 Unicode 无法直接表示二进制数据，因此消息有必要被转换为仅包含 ASCII 字符的数据，这将使用标准电子邮件 RFC 内容传输编码格式技术来编码电子邮件消息以便通过非“8 比特位兼容”的信道来传输。

为了适应 SMIME 签名消息的可重现处理过程，`Generator` 禁用了针对 `multipart/signed` 类型的消息部分及所有子部分的标头折叠。

class email.generator.BytesGenerator(outfp, mangle_from_=None, maxheaderlen=None, *, policy=None)

  返回一个 `BytesGenerator` 对象，该对象将把提供给 `flatten()` 方法的任何消息或者提供给 `write()` 方法的任何经过代理转义编码的文本写入到 file-like object outfp。 outfp 必须支持接受二进制数据的 `write` 方法。

  如果可选的 mangle_from_ 为 `True`，则会将一个 `>` 字符放到消息体中恰好以字符串 `"From "` 打头，即开头文本为 `From` 加一个空格的任何行的前面。 mangle_from_ 默认为 policy 的 `mangle_from_` 设置值 (对于 `compat32` 策略为 `True` 而对于所有其他策略则为 `False`)。 mangle_from_ 被设计为在当消息以 Unix mbox 格式存储时使用 (参见 `mailbox` 和 WHY THE CONTENT-LENGTH FORMAT IS BAD).

  如果 maxheaderlen 不为 `None`，则重新折叠任何长于 maxheaderlen 的标头行，或者如果为 `0`，则不重新包装任何标头。 如果 manheaderlen 为 `None` (默认值)，则根据 policy 设置包装标头和其他消息行。

  如果指定了 policy，则使用该策略来控制消息的生成。如果 policy 为 `None` (默认值)，则使用与传递给 `flatten` 的 `Message` 或 `EmailMessage` 对象相关联的策略来控制消息的生成。 请参阅 `email.policy` 了解有关 policy 所控制内容的详情。

  Added in version 3.2.

  在 3.3 版本发生变更: 添加了 policy 关键字。

  在 3.6 版本发生变更: mangle_from_ 和 maxheaderlen 形参的默认行为是遵循策略。

  flatten(msg, unixfrom=False, linesep=None)

    将以 msg 为根的消息对象结构体的文本表示形式打印到创建 `BytesGenerator` 实例时指定的输出文件。

    如果 `policy` 选项 `cte_type` 为 `8bit` （默认值），则会将未被修改的原始已解析消息中的任何标头拷贝到输出，其中会重新生成与原始数据相同的高比特位组字节数据，并保留具有它们的任何消息体部分的非 ASCII Content-Transfer-Encoding。如果 `cte_type` 为 `7bit`，则会根据需要使用兼容 ASCII 的 Content-Transfer-Encoding 来转换高比特位组字节数据。也就是说，将具有非 ASCII Content-Transfer-Encoding (Content-Transfer-Encoding: 8bit) 的部分转换为兼容 ASCII 的 Content-Transfer-Encoding，并使用 MIME `unknown-8bit` 字符集来编码标头中不符合 RFC 的非 ASCII 字节数据，以使其符合 RFC。

    如果 unixfrom 为 `True`，则会在根消息对象的第一个 RFC 5322 标头之前打印 Unix mailbox 格式 (参见 `mailbox`) 所使用的封包标头分隔符。 如果根对象没有封包标头，则会创建一个标准标头。 默认值为 `False`。 请注意对于子部分来说，不会打印任何封包标头。

    如果 linesep 不为 `None`，则会将其用作扁平化消息的所有行之间的分隔符。如果 linesep 为 `None` （默认值），则使用在 policy 中指定的值。

  clone(fp)

    返回此 `BytesGenerator` 实例的独立克隆，具有完全相同的选项设置，而 fp 为新的 outfp。

  write(s)

    使用 `ASCII` 编解码器和 `surrogateescape` 错误处理程序编码 s，并将其传递给传入到 `BytesGenerator` 的构造器的 outfp 的 write 方法。

作为一个便捷工具，`EmailMessage` 提供了 `as_bytes()` 和 `bytes(aMessage)` (即 `__bytes__()`) 等方法，它们简化了消息对象的序列化二进制表示形式的生成。 更多细节请参阅 `email.message`。

因为字符串无法表示二进制数据，`Generator` 类必须将任何消息中扁平化的任何二进制数据转换为兼容 ASCII 的格式，具体将其转换为兼容 ASCII 的 Content-Transfer_Encoding。 使用电子邮件 RFC 的术语，你可以将其视作 `Generator` 序列化为不 "支持 8 比特" 的 I/O 流。 换句话说，大部分应用程序将需要使用 `BytesGenerator`，而非 `Generator`。

class email.generator.Generator(outfp, mangle_from_=None, maxheaderlen=None, *, policy=None)

  返回一个 `Generator`，它将把提供给 `flatten()` 方法的任何消息，或者提供给 `write()` 方法的任何文本写入到 file-like object outfp。 outfp 必须支持接受字符串数据的 `write` 方法。

  如果可选的 mangle_from_ 为 `True`，则会将一个 `>` 字符放到消息体中恰好以字符串 `"From "` 打头，即开头文本为 `From` 加一个空格的任何行的前面。 mangle_from_ 默认为 policy 的 `mangle_from_` 设置值 (对于 `compat32` 策略为 `True` 而对于所有其他策略则为 `False`)。 mangle_from_ 被设计为在当消息以 Unix mbox 格式存储时使用 (参见 `mailbox` 和 WHY THE CONTENT-LENGTH FORMAT IS BAD).

  如果 maxheaderlen 不为 `None`，则重新折叠任何长于 maxheaderlen 的标头行，或者如果为 `0`，则不重新包装任何标头。 如果 manheaderlen 为 `None` (默认值)，则根据 policy 设置包装标头和其他消息行。

  如果指定了 policy，则使用该策略来控制消息的生成。如果 policy 为 `None` (默认值)，则使用与传递给 `flatten` 的 `Message` 或 `EmailMessage` 对象相关联的策略来控制消息的生成。 请参阅 `email.policy` 了解有关 policy 所控制内容的详情。

  在 3.3 版本发生变更: 添加了 policy 关键字。

  在 3.6 版本发生变更: mangle_from_ 和 maxheaderlen 形参的默认行为是遵循策略。

  flatten(msg, unixfrom=False, linesep=None)

    将以 msg 为根的消息对象结构体的文本表示形式打印到当 `Generator` 实例被创建时所指定的输出文件。

    如果 `policy` 选项 `cte_type` 为 `8bit`，则视同选项被设为 `7bit` 来生成消息。 （这是必需的，因为字符串无法表示非 ASCII 字节数据。）将使用兼容 ASCII 的 Content-Transfer-Encoding 按需转换任何具有高比特位组的字节数据。也就是说，将具有非 ASCII Content-Transfer-Encoding (Content-Transfer-Encoding: 8bit) 的部分转换为兼容 ASCII 的 Content-Transfer-Encoding，并使用 MIME `unknown-8bit` 字符集来编码标头中不符合 RFC 的非 ASCII 字节数据，以使其符合 RFC.

    如果 unixfrom 为 `True`，则会在根消息对象的第一个 RFC 5322 标头之前打印 Unix mailbox 格式 (参见 `mailbox`) 所使用的封包标头分隔符。 如果根对象没有封包标头，则会创建一个标准标头。 默认值为 `False`。 请注意对于子部分来说，不会打印任何封包标头。

    如果 linesep 不为 `None`，则会将其用作扁平化消息的所有行之间的分隔符。如果 linesep 为 `None` （默认值），则使用在 policy 中指定的值。

    在 3.2 版本发生变更: 添加了对重编码 `8bit` 消息体的支持，以及 linesep 参数。

  clone(fp)

    返回此 `Generator` 实例的独立克隆，具有完全相同的选项设置，而 fp 为新的 outfp。

  write(s)

    将 s 写入到传给 `Generator` 的构造器的 outfp 的 write 方法。这为 `Generator` 实例提供了刚好足够可用于 `print()` 函数的文件类 API。

作为一个便捷工具，`EmailMessage` 提供了 `as_string()` 和 `str(aMessage)` (即 `__str__()`) 等方法，它们简化了消息对象的已格式化字符串表示形式的生成。 更多细节请参阅 `email.message`。

`email.generator` 模块还提供了一个派生类 `DecodedGenerator`，它类似于 `Generator` 基类，不同之处在于非 text 部分不会被序列化，而是被表示为 基于模板并填写了有关该部分的信息的字符串输出流的形式。

class email.generator.DecodedGenerator(outfp, mangle_from_=None, maxheaderlen=None, fmt=None, *, policy=None)

  行为类似于 `Generator`，不同之处在于对传给 `Generator.flatten()` 的消息的任何子部分，如果该子部分的主类型为 text，则打印该子部分的已解码载荷，而如果其主类型不为 text，则不直接打印它而是使用来自该部分的信息填入字符串 fmt 并将填写完成的字符串打印出来。

  要填入 fmt，则执行 `fmt % part_info`，其中 `part_info` 是由下列键和值组成的字典：

  - `type` -- 非 text 部分的完整 MIME 类型

  - `maintype` -- 非 text 部分的主 MIME 类型

  - `subtype` -- 非 text 部分的子 MIME 类型

  - `filename` -- 非 text 部分的文件名

  - `description` -- 与非 text 部分相关联的描述

  - `encoding` -- 非 text 部分的内容转换编码格式

  如果 fmt 为 `None`，则使用下列默认 fmt:

  > "[忽略消息的非文本 (%(type)s) 部分，文件名 %(filename)s]"

  可选的 _mangle_from_ 和 maxheaderlen 与 `Generator` 基类的相同。

备注

[1]

此语句假定你使用了正确的 `unixfrom` 设置，并且没有针对自动调整的 `email.policy` 设置调用（例如，`refold_source` 必须为 `none`，这 不是 默认值）。这也不是 100% 为真的，因为如果消息不遵循 RFC 标准则有时实际原始文本的信息会在解析错误恢复时丢失。 目标是在可能的情况下修复这些后续的边缘情况。