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

资讯详情

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

CPython xml.sax.handler 完全解析:SAX 事件处理器基类、特性常量与属性名深度指南

CPython xml.sax.handler 完全解析:SAX 事件处理器基类、特性常量与属性名深度指南 CPython xml.sax.handler 完全解析SAX 事件处理器基类、特性常量与属性名深度指南【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython在 CPython 标准库中xml.sax.handler是 SAX 2 API 的“客户端基座”它定义了五类事件处理器基类ContentHandler、DTDHandler、EntityResolver、ErrorHandler、LexicalHandler并集中声明了全部特性feature与属性property名称常量。掌握这个模块你就能基于xml.sax.parse()/xml.sax.make_parser()构建流式 XML 解析器精确控制命名空间处理、外部实体解析与错误处理策略并了解标准库 Expat 解析器对哪些特性真正生效、哪些会抛出SAXNotSupportedException。一、模块定位SAX 客户端的默认实现骨架xml.sax.handler的源码即 Lib/xml/sax/handler.py其模块 docstring 明确说明This module contains the core classes of version 2.0 of SAX for Python. This file provides only default classes with absolutely minimum functionality, from which drivers and applications can be subclassed. Many of these classes are empty and are included only as documentation of the interfaces.这句话揭示了该模块的设计哲学所有基类方法都是“空壳”或“最小行为”默认实现。应用方只需继承基类并重写自己关心的事件方法其余方法自动获得默认行为不会因缺少实现而抛错。从 Lib/xml/sax/init.py 的包文档也可印证该模块在 SAX 体系中的角色handler -- Base classes and constants which define the SAX 2 API for the client-side of SAX for Python.SAX 定义了五类处理器接口应用通常只实现自己感兴趣的事件接口且可以把多个接口实现集中在同一个对象里也可以分散到多个对象中。官方文档要求处理器实现应当继承xml.sax.handler提供的基类这样所有方法都有默认实现。二、五个处理器基类总览xml.sax.handler提供的五个类源码见 Lib/xml/sax/handler.py类职责注册方式ContentHandler主回调接口事件顺序与文档信息顺序一致XMLReader.setContentHandler()DTDHandler处理 DTD 事件仅基本解析所需的 notation 与 unparsed entityXMLReader.setDTDHandler()EntityResolver解析外部实体的系统标识符XMLReader.setEntityResolver()ErrorHandler接收警告/可恢复错误/致命错误三级信息XMLReader.setErrorHandler()LexicalHandler低频词法事件注释、CDATA、DTD 边界XMLReader.setProperty(property_lexical_handler, …)前四类 handler 的注册由 Lib/xml/sax/xmlreader.py 中的XMLReader基类完成——XMLReader.__init__默认就为四种角色各实例化了一个handler模块中的空基类对象def __init__(self): self._cont_handler handler.ContentHandler() self._dtd_handler handler.DTDHandler() self._ent_handler handler.EntityResolver() self._err_handler handler.ErrorHandler()而LexicalHandler是可选扩展必须通过属性机制挂载见第六节。2.1 ContentHandlerSAX 的核心回调接口ContentHandler是 SAX 中最主要的回调接口也是应用方最重要的接口。文档明确指出该接口的事件顺序镜像文档中信息的顺序先出现的事件对应文档中靠前的内容。从源码看它还在__init__中维护了一个_locator属性保存解析器通过setDocumentLocator传入的定位器引用。2.2 DTDHandler仅覆盖基本 DTD 事件DTDHandler只处理基本解析所需的 DTD 事件unparsed entity 与 attribute 相关。它只有两个方法notationDecl与unparsedEntityDecl在标准库实现中由 Expat 的NotationDeclHandler/UnparsedEntityDeclHandler回调触发Lib/xml/sax/expatreader.py。2.3 EntityResolver外部实体的统一入口实现EntityResolver并将其注册到解析器后解析器会通过它的方法解析所有外部实体。其默认实现直接返回systemId即“按原样读取”class EntityResolver: def resolveEntity(self, publicId, systemId): Resolve the system identifier of an entity and return either the system identifier to read from as a string, or an InputSource to read from. return systemId在 Expat 驱动中external_entity_ref回调展示了完整的调用链Lib/xml/sax/expatreader.py若feature_external_ges未开启则直接return 1跳过实体否则调用self._ent_handler.resolveEntity(pubid, sysid)用返回的字符串或InputSource构造新来源切换 expat 解析器上下文后递归解析实体内容并在结束后恢复原解析器状态。2.4 ErrorHandler三级错误与默认行为ErrorHandler是解析器向应用呈递错误/警告信息的接口其方法控制错误是否立即转化为异常。三个等级warning、可能可恢复的error、不可恢复的 fatalError。所有方法都只接收一个xml.sax.SAXParseException参数抛出传入的异常对象即可把错误/警告转换为异常。标准库的默认实现Lib/xml/sax/handler.py值得逐行记住class ErrorHandler: def error(self, exception): Handle a recoverable error. raise exception def fatalError(self, exception): Handle a non-recoverable error. raise exception def warning(self, exception): Handle a warning. print(exception)error()遇到可恢复错误时调用。若不抛异常解析可能继续但不应再指望后续文档信息的完整性让解析器继续运行可以暴露输入文档中的更多错误。fatalError()遇到无法恢复的错误时调用该方法返回后解析预期终止。warning()解析器呈递轻微警告解析预期继续。在该方法内抛异常会导致解析结束。2.5 LexicalHandler词法事件的可选扩展LexicalHandler是 SAX2 的可选处理器用于获取文档的词法信息文档编码描述、嵌入的 XML 注释、DTD 与 CDATA 分区的边界。它的使用方式与 content handler 相同继承后重写方法但注册途径不同——文档明确规定通过XMLReader.setProperty()挂载属性标识符为http://xml.org/sax/properties/lexical-handler即模块常量property_lexical_handler。三、ContentHandler 方法逐个详解用户预期通过继承ContentHandler来支持应用。以下方法由解析器在输入文档的相应事件发生时调用行号以 Lib/xml/sax/handler.py 为准。3.1 文档边界事件setDocumentLocator(locator)解析器借此给应用传递定位器用于定位文档事件的来源。SAX 解析器被“强烈建议”但非绝对强制提供 locator若提供必须在调用DocumentHandler接口中任何其他方法之前调用此方法。注意locator 只在事件调用期间返回正确信息其他时间不应使用。在 Expat 驱动中parse()开头即执行self._cont_handler.setDocumentLocator(ExpatLocator(self))Lib/xml/sax/expatreader.pyExpatLocator内部用弱引用持有解析器以避免循环引用。startDocument()文档开始通知。SAX 解析器只调用一次且在本接口或 DTDHandler 的所有其他方法之前setDocumentLocator除外。endDocument()文档结束通知。同样只调用一次且是解析期间调用的最后一个方法除非解析器放弃解析不可恢复错误或到达输入末尾否则不应调用它。Expat 驱动在close()中触发先feed(b, isFinalTrue)做最终校验再调用endDocument()Lib/xml/sax/expatreader.py。3.2 命名空间前缀映射事件startPrefixMapping(prefix, uri)开始一个 prefix-URI 命名空间映射的作用域。该信息对常规命名空间处理并非必需当feature_namespaces特性开启时Expat 驱动的默认是关闭见ExpatParser.__init__(namespaceHandling0)由xml.sax的parse()统一开启SAX reader 会自动替换元素名和属性名中的前缀。但存在特殊场景——应用需要在字符数据或属性值中使用前缀这些位置无法被安全地自动展开此时startPrefixMapping/endPrefixMapping事件提供应用自行展开前缀所需的信息。文档特别强调嵌套保证是受限的所有startPrefixMapping事件都发生在对应startElement之前所有endPrefixMapping事件都发生在对应endElement之后但它们之间的相对顺序不保证。endPrefixMapping(prefix)结束一个 prefix-URI 映射的作用域。该事件总发生在对应的endElement之后但事件之间的顺序无其他保证。在 Expat 驱动中这两个事件由 expat 的StartNamespaceDeclHandler/EndNamespaceDeclHandler直接映射Lib/xml/sax/expatreader.py。3.3 元素事件startElement(name, attrs)非命名空间模式name是元素类型的原始 XML 1.0 名称字符串attrs持有实现 Attributes 接口的对象包含该元素的属性。关键陷阱解析器可能复用attrs对象持有对它的引用并不是保存属性副本的可靠方式——如需保留副本必须调用attrs对象的copy()方法。endElement(name)元素结束非命名空间模式name参数与startElement相同。startElementNS(name, qname, attrs)命名空间模式name是(uri, localname)元组qname是源文档中使用的原始 XML 1.0 名称attrs是实现 AttributesNS 接口的实例。若元素未关联命名空间name的uri分量为None。attrs的复用警告与copy()要求同上。解析器可能把qname设为None——除非启用了feature_namespace_prefixes特性。endElementNS(name, qname)命名空间模式的元素结束参数含义同startElementNS。从源码看Expat 驱动在非命名空间模式下调用startElement(name, AttributesImpl(attrs))Lib/xml/sax/expatreader.py其中AttributesImpl定义在 Lib/xml/sax/xmlreader.py其copy()方法return self.__class__(self._attrs)印证了文档中“用copy()保存副本”的用法命名空间模式下则传入AttributesNSImpl(newattrs, qnames)Lib/xml/sax/xmlreader.py。3.4 字符数据与其他事件characters(content)接收字符数据通知。解析器可能把所有连续字符数据放在一个 chunk 里返回也可能拆分为多个 chunk但任何单次事件中的所有字符必须来自同一个外部实体以保证 Locator 信息有用。content可以是str或bytes实例expat reader 模块总是产生字符串。文档还附有一条历史注记早期 Python XML SIG 的 SAX 1 接口采用更 Java 风格的方法签名content, offset, length切片式新接口选择了更简单的签名迁移旧代码时直接用content即可不再需要 offset/length 切片。ignorableWhitespace(whitespace)接收元素内容中可忽略空白的通知。校验解析器必须用它报告每个可忽略空白块见 W3C XML 1.0 推荐规范 2.10 节非校验解析器若能解析并使用内容模型也可使用该方法。分块规则与characters相同。processingInstruction(target, data)每条处理指令都会触发一次处理指令可以出现在主文档元素之前或之后。SAX 解析器不应通过该方法报告 XML 声明XML 1.0 2.8 节或文本声明XML 1.0 4.3.1 节。skippedEntity(name)每个被跳过的实体触发一次。非校验处理器若未见过实体声明例如实体声明在外部 DTD 子集中可以跳过实体所有处理器都可以跳过外部实体具体取决于feature_external_ges与feature_external_pes特性值。源码中 Expat 驱动对参数实体PE会在名称前加%以满足 SAX 规范Lib/xml/sax/expatreader.py。四、DTDHandler、EntityResolver 与 ErrorHandler 方法速查4.1 DTDHandlernotationDecl(name, publicId, systemId)处理 notation 声明事件。unparsedEntityDecl(name, publicId, systemId, ndata)处理 unparsed entity 声明事件。4.2 EntityResolverresolveEntity(publicId, systemId)解析实体的系统标识符返回一个可读的字符串系统标识符或返回一个可读的InputSource。默认实现返回systemId。InputSource定义在 Lib/xml/sax/xmlreader.py支持 public/system 标识符、编码、字节流与字符流四类信息注意应用传给XMLReader的InputSource不被允许被解析器修改解析器只能修改自己复制的副本。4.3 ErrorHandler三个方法error(exception)、fatalError(exception)、warning(exception)的语义已在 2.4 节结合源码详述。标准库中xml.sax顶层还直接导出了ErrorHandlerLib/xml/sax/init.pyxml.sax.parse()与parseString()的默认errorHandler参数就是ErrorHandler()实例。五、特性常量feature完整清单除五个类外xml.sax.handler还提供特性与属性名的符号常量。以下每个常量均给出精确字符串值、true/false 语义与访问权限源码 Lib/xml/sax/handler.py常量字符串值true 语义false 语义访问feature_namespaceshttp://xml.org/sax/features/namespaces执行命名空间处理可选地不处理命名空间蕴含 namespace-prefixes默认解析中只读未解析时读写feature_namespace_prefixeshttp://xml.org/sax/features/namespace-prefixes报告用于命名空间声明的原始带前缀名称与属性不报告命名空间声明属性可选地不报告原始带前缀名称默认解析中只读未解析时读写feature_string_interninghttp://xml.org/sax/features/string-interning所有元素名、前缀、属性名、命名空间 URI 与本地名都在字典中做 interning见property_interning_dict名称不保证 interning但可能默认解析中只读未解析时读写feature_validationhttp://xml.org/sax/features/validation报告所有校验错误蕴含 external-general-entities 与 external-parameter-entities不报告校验错误解析中只读未解析时读写feature_external_geshttp://xml.org/sax/features/external-general-entities包含所有外部一般文本实体不包含外部一般实体解析中只读未解析时读写feature_external_peshttp://xml.org/sax/features/external-parameter-entities包含所有外部参数实体含外部 DTD 子集不包含任何外部参数实体连外部 DTD 子集也不含解析中只读未解析时读写all_features是包含上述六个特性名称的列表可用于枚举或初始化场景。5.1 标准库 Expat 解析器的实际支持边界文档对三个特性都附有“标准库 Expat 解析器不支持”的说明Lib/xml/sax/expatreader.py 的getFeature/setFeature实现给出了精确的代码证据feature_namespace_prefixesgetFeature返回 0setFeature置真时抛出SAXNotSupportedException(expat does not report namespace prefixes)。因此startElementNS的qname参数在 Expat 驱动下恒为None见 Lib/xml/sax/expatreader.py 传入字面量None。feature_validationexpat 是非校验解析器置真时抛出SAXNotSupportedException(expat does not support validation)。feature_external_pesexpat 不读取外部参数实体置真时抛出SAXNotSupportedException。feature_external_ges可读可写由self._external_ges标志控制未开启时外部实体引用直接被跳过external_entity_ref返回 1。feature_string_interning置真会创建一个空字典作为 interning 字典读取时等价于self._interning is not None。feature_namespaces可读可写切换底层expat.ParserCreate的命名空间模式见reset()。此外setFeature在解析进行中会被一律拒绝SAXNotSupportedException(Cannot set features while parsing)与文档“解析中只读”的访问描述一致。5.2feature_external_ges的安全警告文档对该特性附了一条醒目的警告框Enabling opens a vulnerability to external entity attacks if the parser is used with user-provided XML content. Please reflect on your threat model before enabling this feature.即如果解析器用于处理用户提供的 XML 内容开启外部一般实体解析会打开“外部实体攻击”XXE的口子。因此处理不可信输入时应保持该特性为默认关闭状态这正是标准库 Expat 驱动的默认行为。六、属性常量property完整清单属性常量源码 Lib/xml/sax/handler.py通过XMLReader.getProperty/setProperty访问常量字符串值数据类型说明访问property_lexical_handlerhttp://xml.org/sax/properties/lexical-handlerLexicalHandler处理注释等词法事件的可选扩展处理器读写property_declaration_handlerhttp://xml.org/sax/properties/declaration-handler实现 SAX2DeclHandler接口的对象处理 notation 与 unparsed entity 之外 DTD 相关事件的可选扩展处理器读写property_dom_nodehttp://xml.org/sax/properties/dom-nodexml.dom.Node解析中为 DOM 迭代器当前访问的节点未解析时为待迭代根节点解析中只读未解析时读写property_xml_stringhttp://xml.org/sax/properties/xml-stringBytes当前事件来源的字面字符串只读且仅在 handler 回调期间有效property_encodinghttp://www.python.org/sax/properties/encodingString输入数据应采用的编码名读写property_interning_dicthttp://www.python.org/sax/properties/interning-dictDictionary用于 intern 名称的字典名称未 interning 时为None。设置它会启用 interningfeature_string_interning特性同样如此读写文档同时给出三条重要的支持边界说明property_declaration_handler标准库中没有任何解析器支持该属性标准库也不提供对应 handler。property_dom_node标准库中没有任何解析器支持该属性。property_encoding标准库中没有任何解析器支持该属性。也就是说实际可用的是property_lexical_handler、property_xml_string与property_interning_dict。all_properties常量列出全部已知属性名。Expat 驱动的getProperty/setPropertyLib/xml/sax/expatreader.py印证了这些边界property_lexical_handler读写均支持解析中设置时会通过_reset_lex_handler_prop()立即把comment/startCDATA/endCDATA/startDTD/endDTD五个回调挂到 expat 解析器上或置 None 解挂。property_interning_dict读写均支持直接读写self._interning并透传给expat.ParserCreate(..., internself._interning)。property_xml_string只读解析中通过self._parser.GetInputContext()返回当前输入上下文字符串未解析时抛SAXNotSupportedException尝试写入则抛SAXNotSupportedException(Property %s cannot be set)。七、LexicalHandler 方法详解文档对 LexicalHandler 的定义它是 SAX2 的词法事件可选处理器用于获取 XML 文档的词法信息文档编码描述、嵌入注释、DTD 与 CDATA 分区的边界使用方式与 content handler 相同。注册方法使用setProperty属性标识符为http://xml.org/sax/properties/lexical-handler源码常量property_lexical_handler。五个方法源码 Lib/xml/sax/handler.pycomment(content)报告文档任意位置含 DTD 内与文档元素外的注释content是持有注释内容的字符串。startDTD(name, public_id, system_id)若文档有关联 DTD报告 DTD 声明开始。name是文档元素类型名public_id是 DTD 的公共标识符未提供时为Nonesystem_id是外部子集的系统标识符未提供时为None。在 Expat 驱动中该事件由start_doctype_decl回调转发生成Lib/xml/sax/expatreader.py。endDTD()报告 DTD 声明结束。startCDATA()报告 CDATA 标记节开始CDATA 节的内容本身通过characters事件报告。endCDATA()报告 CDATA 标记节结束。八、实战示例从xml.sax.parse到自定义处理器xml.sax.handler常与 Lib/xml/sax/init.py 中的高层入口配合使用。parse(source, handler, errorHandlerErrorHandler())用默认解析器解析 XMLsource是系统标识符、path-like 对象或文件对象handler是 ContentHandler 实例errorHandler是 ErrorHandler 实例“所有工作都由 handler 完成”parseString则接受 str 或 bytes-like 对象内部通过InputSource的字符流/字节流封装后走同样的make_parser()路径。默认解析器列表是[xml.sax.expatreader]且可通过环境变量PY_SAX_PARSER逗号分隔的模块名列表覆盖。下面是一个完整可运行的示例演示 ContentHandler ErrorHandler LexicalHandler 的组合使用import io import xml.sax import xml.sax.handler class MyHandler(xml.sax.handler.ContentHandler, xml.sax.handler.ErrorHandler, xml.sax.handler.LexicalHandler): 把三类接口合并在一个对象中只重写关心的事件。 def startElement(self, name, attrs): print(f{name}, dict(attrs)) # attrs 支持 dict(attrs) 快照 self._tag name def characters(self, content): # 解析器可能分片推送需自行累积expat reader 总是产生 str if getattr(self, _tag, None): print(repr(content)) def endElement(self, name): print(f/{name}) # ErrorHandler 三级错误处理默认行为即抛异常这里显式记录 def warning(self, exception): print(warning:, exception) # 不抛出 解析继续 def error(self, exception): raise exception # 默认行为转为异常 def fatalError(self, exception): raise exception # LexicalHandler接收注释 def comment(self, content): print(-- comment:, content) if __name__ __main__: parser xml.sax.make_parser() parser.setProperty( xml.sax.handler.property_lexical_handler, MyHandler()) handler MyHandler() xml.sax.parseString( root a1!-- note --itemhello/item/root, handler, errorHandlerMyHandler())要点回顾多个接口可以合并在同一个类里文档明确允许“in a single object or in multiple objects”每个未重写的方法都继承自xml.sax.handler基类的空实现characters()可能被分片调用应用需按业务需要累积缓冲注释、CDATA 等词法事件必须先通过setProperty(xml.sax.handler.property_lexical_handler, ...)注册才会送达对不可信输入切勿setFeature(xml.sax.handler.feature_external_ges, True)。测试用例 Lib/test/test_sax.py 与 Lib/test/test_pulldom.py 展示了特性常量的真实使用方式例如parser.setFeature(xml.sax.handler.feature_namespaces, 1)pulldom 中在构造PullDOM解析器时调用Lib/xml/dom/pulldom.py。九、关键陷阱与注意事项汇总attrs 对象会被复用startElement/startElementNS传入的attrs可能被解析器复用必须调用copy()保存副本AttributesImpl.copy()返回同类的浅封装副本。Expatri 前缀名不可得startElementNS的qname在 Expat 驱动下恒为None因为feature_namespace_prefixes不受支持feature_validation与feature_external_pes同理不可开启会抛SAXNotSupportedException。locator 有时效性setDocumentLocator传入的定位器只在事件回调期间有效。解析中改特性会被拒绝setFeature在self._parsing期间一律抛异常而setContentHandler/setProperty(property_lexical_handler)允许解析中热切换Expat 驱动会立即重挂底层回调Lib/xml/sax/expatreader.py。characters的类型content可为 str 或 bytes但 expat reader 总是产生字符串旧 SAX 1 的(content, offset, length)签名已被单一content参数取代。默认 ErrorHandler 的严格性默认error()与fatalError()都直接抛出传入异常warning()打印到 stdout若想容忍可恢复错误需重写error()并不抛出。十、延伸阅读路径Lib/xml/sax/handler.py本文全部基类与常量的一手源码。Lib/xml/sax/xmlreader.pyXMLReader、IncrementalParser、Locator、InputSource、AttributesImpl/AttributesNSImpl基类。Lib/xml/sax/expatreader.py标准库唯一的默认 SAX 驱动展示 handler 回调与 feature/property 的落点。Lib/xml/sax/saxutils.pyInputSource准备、转义工具等配套类。Lib/xml/sax/_exceptions.pySAXParseException等异常类型ErrorHandler 各方法的参数类型。Doc/library/xml.sax.rst 与 Doc/library/xml.sax.xmlreader.rst、Doc/library/xml.sax.saxutils.rstSAX 包其余模块的官方文档。Lib/test/test_sax.pySAX 解析行为含 feature 设置的回归测试。【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表