尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

RustPython 标准库 email 包架构解析:Model、Parser、Generator 与 Policy 四层协作机制

RustPython 标准库 email 包架构解析:Model、Parser、Generator 与 Policy 四层协作机制 RustPython 标准库 email 包架构解析Model、Parser、Generator 与 Policy 四层协作机制【免费下载链接】RustPythonA Python Interpreter written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/RustPython导读本文以 RustPython 仓库中 Lib/email/architecture.rst 这一官方架构文档为骨架深入剖析email包的核心设计以Message模型为中心的 Model、Parser、Generator 三大组件以及贯穿消息生命周期、控制解析与序列化行为的 Policy 机制。读完本文你将掌握 email 包的分层架构、四个 Policy 钩子方法header_source_parse、header_store_parse、header_fetch_parse、fold/fold_binary的调用时机与语义理解surrogateescape二进制数据携带机制并能结合 Lib/email/_policybase.py 与 Lib/email/policy.py 的源码在 RustPython 解释器中正确选用compat32、default、SMTP、HTTP等内置策略处理邮件消息。总体架构三大功能组件 一个关键组件email包由三大功能组件构成外加一个贯穿全局的关键组件 Policy组件职责仓库中的核心实现Model模型表示一封邮件消息的对象结构提供创建、查询、修改消息的 APILib/email/message.py 中的Message/EmailMessage类Parser解析器接收字符或字节序列产出对应的消息模型Lib/email/parser.py 中的Parser、BytesParser、HeaderParser、FeedParserGenerator生成器接收模型产出一段字符或字节序列供人阅读或按 RFC 规定的内容传输编码Content Transfer Encoding后上线传输Lib/email/generator.py 中的Generator、BytesGeneratorPolicy策略指定各种行为设置并携带各种控制行为的算法实现Lib/email/_policybase.py 与 Lib/email/policy.py 中的Policy、Compat32、EmailPolicy从概念上讲整个包围绕Model组织模型同时提供两类 API——供应用程序使用的外部 API以及供 Parser 和 Generator 组件使用的内部 API。这个划分刻意保持一定的模糊性文档声明的 API 全部是公开、稳定的。这意味着一个有特殊需求的应用程序完全可以实现自己的 Parser 和/或 Generator只要它遵守模型两侧的接口约定即可。Policy 框架的存在让库的行为可以通过一个简单统一的对象来控制既能在共享解析、表示、生成通用代码的前提下以极其灵活的方式使用库又能针对不同场景定制行为。除了默认的 RFC 5322 邮件策略外仓库还提供了一个按 RFC 2616 管理 HTTP 头的策略见下文HTTP实例。单个策略控制项例如 Generator 产出的最大行长max_line_length也可以单独调整以满足特定应用需求。ModelMessage对象与消息的两分法模型由Message类实现Lib/email/message.py#L141-L156。模型按照 RFC 的表述把消息分为两个基本部分头部header section与主体body。头部保持顺序的伪字典Message对象充当一个命名头部的伪字典pseudo-dictionary。它的字典接口提供按名称便捷访问单个头部的能力。然而所有头部在内部都保存在一个有序列表中以保留头部在原始消息中的顺序信息。这一点在源码中清晰可见def __init__(self, policycompat32): self.policy policy self._headers [] # 头部存储在有序列表中 self._unixfrom None self._payload None # 主体负载 ...同时Message类文档Lib/email/message.py#L150-L155也提醒字典mapping接口假定每个头部在一条消息中只出现一次但有些头部确实会出现多次例如Received对这些头部必须使用显式 API 来设置或获取全部实例。主体payload 的两种形态Message对象还有一个payload属性保存主体。payload只能是两种东西之一数据data即字符串主体Message对象列表用于表示 multipart MIME 消息。列表可以嵌套任意深度来表示消息所有终端叶子节点都持有非列表形式的数据 payload。源码中is_multipart()的判断正基于此Lib/email/message.py#L217-L219def is_multipart(self): Return True if the message consists of multiple parts. return isinstance(self._payload, list)attach()方法用于向 multipart 消息追加子对象get_payload(i, decode)支持按索引取子部分并可依据Content-Transfer-Encoding头解码Lib/email/message.py#L233-L260。在新的EmailMessage类message_factory EmailMessage中还提供了get_content/set_content高级接口它们把实际工作委托给 policy 上的content_manager默认是 Lib/email/contentmanager.py 中的raw_data_manager。消息生命周期创建 → 操纵 → 终结消息的一般生命周期分为三个阶段创建CreationMessage对象可以由 Parser 创建也可以由应用程序直接实例化为空消息。操纵Manipulation应用程序可以检查一个或多个头部和/或 payload也可以修改它们。这一操作既可以在顶层Message对象上进行也可以在任意子对象上进行。终结Finalization模型被转换为 unicode 或二进制流通过as_string/as_bytes见 Lib/email/message.py#L173-L215或者模型被丢弃。as_string内部会构造一个Generator并把消息flatten到StringIOas_bytes则构造BytesGenerator输出到BytesIO。二者默认使用与消息实例关联的 policy也可显式传入 policy 参数覆盖。Policy 在生命周期中的头部控制四个1 个钩子Policy 行使的主要控制之一就是在Message生命周期中管理头部。大多数应用程序不需要感知到这一层但理解它对于实现自定义 Policy 至关重要。一个头部以两种方式进入模型经由Parser解析而来由应用程序在模型已存在后设置一个具体值。同理一个头部以两种方式离开模型被Generator序列化被应用程序从模型中取回。Policy 对象为这四条通路全部提供了钩子。模型的头部存储形式是(name, value)元组列表。五个钩子方法及其职责如下抽象方法定义见 Lib/email/_policybase.py#L237-L285钩子方法触发时机职责header_source_parse(sourcelines)Parser 解析出头部行时接收一个带行终止符的头部各行字符串列表返回要存入模型的(name, value)元组header_store_parse(name, value)应用程序通过__setitem__等接口设置头部时返回要存入模型的(name, value)元组header_fetch_parse(name, value)应用程序通过字典/列表接口取回头部时把模型中的值转换为返回给应用程序的值返回值不应再含 surrogateescape 数据fold(name, value)Generator 序列化请求头部时返回在合适位置插入换行的字符串含折叠后的行分隔符fold_binary(name, value)产出二进制输出的 Generator 请求头部时返回二进制形式的折叠头部可能在不同于字符串版的位置折叠此外还有两个关键控制点cte_typePolicy 控制决定是否对头部数据执行 Content Transfer Encoding。binary_fold供产生二进制输出的生成器使用返回二进制折叠结果。handle_defect(obj, defect)与register_defect(obj, defect)则负责 RFC 违规的处理若raise_on_defect为真则直接抛出否则登记到对象的defects列表Lib/email/_policybase.py#L186-L216。Policy 的可配置属性一览Policy基类Lib/email/_policybase.py#L140-L184定义了以下可设置属性属性默认值含义raise_on_defectFalse为真时 RFC 违规以错误形式抛出否则登记为 defectlinesep\n输出行之间的分隔字符串cte_type8bit允许的内容传输编码类型7bit仅 ASCII或8bit允许Content-Transfer-Encoding: 8bit同时控制头部中RFC 违规的二进制数据的处置max_line_length78序列化时除linesep外的最大行长None或0表示不做换行包装mangle_from_False为真时在消息正文中以转义From_行message_factoryNone用于创建新消息对象的类为None时默认Messageverify_generated_headersTrue为真时生成器校验每个头部折叠正确防止 Parser 将其误判为多个头部、正文开始或另一头部的一部分_PolicyBase实现了 Policy 对象的三种关键操作Lib/email/_policybase.py#L27-L100不可变__setattr__被重写为直接抛AttributeErrorPolicy 对象创建后属性只读克隆clone(**kw)返回仅更改指定属性的新实例相加A B等价于A(B 中的非默认值)右侧操作数的非默认值覆盖左侧这在组合多个策略时非常有用。二进制数据处理surrogateescape 携带机制理想情况下所有消息数据都符合 RFCParser 能把消息解码成发送者最初书写的理想 unicode 消息。现实世界则要求 email 包能处理格式糟糕的消息包括含有非 ASCII 字符、但没有声明字符集、或字符不在所声明字符集内的消息。由于邮件消息本质上是文本数据对消息数据的操作也主要是文本操作二进制 payload 除外因此模型把所有文本数据存储为 unicode 字符串。文本数据中不可解码的二进制通过 ASCII 编解码器的surrogateescape错误处理器处理。这与该错误处理器最初为二进制文件名引入的用途一致让 email 包在解析阶段携带收到的二进制数据一路带到输出阶段在输出时再以其原始形式重新生成。这种被携带的二进制数据几乎完全是实现细节。它在 API 中唯一可见的地方是内部 APIParser 必须对二进制输入数据做surrogateescape编码然后把数据传给相应的 Policy 方法Generator 用于访问头部值的内部接口会保留 surrogateescaped 字节其他所有接口则把二进制数据转回字节或安全形式某些情况下会丢失信息。源码中的判定助手是email.utils._has_surrogatesCompat32._sanitize_header用它检测值中的 surrogate 数据并将其包装为使用unknown-8bit字符集的Header对象Lib/email/_policybase.py#L298-L308。后向兼容Compat32 策略Compat32策略Lib/email/_policybase.py#L288-L389通过如下五个方法的实现与 email 包 5.1 版本保持后向兼容header_source_parse在第一行的冒号处分割得到 name丢弃冒号后的所有空格把本行其余部分与所有后续行连接起来保留 linesep 字符以得到 value从最终 value 字符串剥离尾部的回车和/或换行符。源码实现def header_source_parse(self, sourcelines): name, value sourcelines[0].split(:, 1) value .join((value, *sourcelines[1:])).lstrip( \t\r\n) return (name, value.rstrip(\r\n))header_store_parse返回应用程序传入的 name 和 value原样不动仅校验头部名合法性def header_store_parse(self, name, value): validate_header_name(name) return (name, value)header_fetch_parse若 value 含 surrogateescaped 二进制数据则以unknown-8bit字符集返回Header对象否则原样返回。fold使用Header类的折叠算法以与 email5.1 生成器相同的方式折叠头部保留值中已有的换行并把每行包装到max_line_length非 ASCII 二进制数据用unknown-8bit字符集做 CTE 编码。binary_fold与fold相同但编码为asciidef fold_binary(self, name, value): folded self._fold(name, value, sanitizeself.cte_type7bit) return folded.encode(ascii, surrogateescape)注意Compat32把mangle_from_默认值改为TrueLib/email/_policybase.py#L296以复刻旧版行为。新算法EmailPolicyEmailPolicyLib/email/policy.py#L33-L97引入了新的头部解析与折叠算法头部不再是简单字符串而是根据不同字段类型带有自定义属性的头部对象折叠算法完整实现 RFC 2047 与 RFC 5322。其五个钩子实现如下header_source_parse与旧版行为相同见Compat32的header_source_parse。header_store_parse与旧版行为相同但若输入值带有与 name忽略大小写匹配的name属性则原样返回否则把 name 和 value 交给header_factory生成自定义头部对象。此时若输入 value 含 CR 或 LF 字符会抛出ValueErrorLib/email/policy.py#L137-L155def header_store_parse(self, name, value): validate_header_name(name) if hasattr(value, name) and value.name.lower() name.lower(): return (name, value) if isinstance(value, str) and len(value.splitlines())1: raise ValueError(Header values may not contain linefeed or carriage return characters) return (name, self.header_factory(name, value))header_fetch_parse若 value 已经是头部对象则直接返回否则用新解析器解析 value 并返回结果对象surrogateescaped 字节被转换为 unicode 未知字符码点。fold使用新头部折叠算法并尊重 policy 设置。surrogateescaped 字节在cte_type7bit或8bit时用unknown-8bit字符集编码返回字符串。文档还预告了未来的cte_typeunicode届时 fold 将按 RFC 风格折叠序列化理想化的 unicode 消息把 surrogateescaped 字节转换为 unicode 未知字符字形。折叠决策由_fold根据refold_source与行长判定Lib/email/policy.py#L211-L230。binary_fold使用新折叠算法并尊重 policy 设置。surrogateescaped 字节在cte_type7bit时用unknown-8bit字符集编码在cte_type8bit时转回字节返回 bytes。若utf8为真则编码为 utf8否则编码为 ascii 并将非 ASCII unicode 渲染为编码词Lib/email/policy.py#L193-L209def fold_binary(self, name, value): folded self._fold(name, value, refold_binaryself.cte_type7bit) charset utf8 if self.utf8 else ascii return folded.encode(charset, surrogateescape)文档同样预告未来的cte_typeunicode将使binary_fold按 RFC 5335 序列化消息。EmailPolicy额外引入三个属性属性默认值含义utf8False为False时头部序列化为 ASCII非 ASCII 字符用编码词为True时头部用 utf8 序列化且不含编码词RFC 6532 格式refold_sourcelong来自解析源的头部值是否在生成时重新折叠none全部保留原折叠long仅对存在超过max_line_length的行的值重折叠all全部重折叠header_factoryHeaderRegistry()接收name与value返回头部对象的可调用对象默认工厂理解部分 RFC 5322 头部类型目前地址字段与日期字段有特殊处理content_managerraw_data_manager至少含get_content/set_content两个方法的对象Message.get_content/set_content会委托给它此外EmailPolicy实现了header_max_count(name)返回构造某类头部所对应专用类的max_count属性从而限制单条消息中某些头部如Received可编程添加的数量上限解析器不受此限制。内置策略实例仓库 Lib/email/policy.py#L233-L239 提供了开箱即用的策略实例default EmailPolicy() # Make the default policy use the class default header_factory del default.header_factory strict default.clone(raise_on_defectTrue) SMTP default.clone(linesep\r\n) HTTP default.clone(linesep\r\n, max_line_lengthNone) SMTPUTF8 SMTP.clone(utf8True)实例构造方式适用场景defaultEmailPolicy()新式EmailMessage模型的默认策略删除显式header_factory以使用类级默认工厂strictdefault.clone(raise_on_defectTrue)严格模式RFC 违规直接抛错而非登记 defectSMTPdefault.clone(linesep\r\n)SMTP 传输行分隔符按 RFC 5322 使用 CRLFHTTPdefault.clone(linesep\r\n, max_line_lengthNone)HTTP 头部管理RFC 2616 兼容不做行长折叠SMTPUTF8SMTP.clone(utf8True)SMTPUTF8RFC 6531/6532允许 utf8 头部序列化注意SMTPUTF8是基于SMTP克隆的因此同时继承 CRLF 行分隔符与 utf8 输出。与之对照compat32单例Compat32实例则作为Parser和Message的默认 policyLib/email/parser.py#L17、Lib/email/message.py#L156确保旧式 API 行为不变。实战串联解析、操纵、生成与策略切换将上述架构应用到实际流程中一个典型的 RustPython 消息处理流程如下from email import policy from email.parser import BytesParser # 1. 创建BytesParser 产出模型内部通过 policy.header_source_parse 处理每个头部 msg BytesParser(policypolicy.default).parsebytes(raw_bytes) # 2. 操纵dict 接口读写头部经 policy.header_store_parse / header_fetch_parse msg[Subject] Hello 世界 subject msg[Subject] # 新策略下返回自定义头部对象 # 3. 终结as_bytes 内部构造 BytesGenerator经 policy.fold_binary 折叠头部 wire msg.as_bytes(policypolicy.SMTP) # CRLF 行分隔、ASCII 编码词 # 旧式 API 保持兼容 from email.parser import Parser old Parser().parsestr(text) # 默认 compat32 策略整个流程中模型内部的(name, value)元组列表保持头部顺序Policy 的五个钩子分别掌管头部进入—存储—取回—序列化的每一站而surrogateescape则保证解析阶段无法解码的二进制数据能被原样带到输出阶段重新生成。小结RustPython 的 email 包以 Model 为核心、Policy 为行为控制器形成一条清晰的职责链Parser 把字符/字节流翻译成模型Generator 把模型翻译回流而 Policy 在这条链的四个接缝处source 解析、store 写入、fetch 取回、fold 序列化以五个钩子方法精确控制头部的每一次进出。理解这套架构既能在 RustPython 中正确选用compat32、default、SMTP、HTTP、SMTPUTF8、strict等内置策略也能通过子类化Policy/EmailPolicy自定义解析与生成行为满足诸如自定义行长、utf8 头部或 HTTP 头部管理等特殊应用需求。参考文件索引架构文档Lib/email/architecture.rstPolicy 基类与 Compat32Lib/email/_policybase.pyEmailPolicy 与内置策略实例Lib/email/policy.pyMessage 模型Lib/email/message.pyParser / BytesParser / FeedParserLib/email/parser.pyGenerator / BytesGeneratorLib/email/generator.py内容管理器Lib/email/contentmanager.py头部对象与注册表Lib/email/headerregistry.py【免费下载链接】RustPythonA Python Interpreter written in Rust项目地址: https://gitcode.com/GitHub_Trending/ru/RustPython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表