无法调试xml.etree.ElementTree及自定义XMLParser方法不生效排查
无法调试ElementTree类及自定义XMLParser子类方法不触发问题
问题场景
初始测试代码:
from xml.etree import ElementTree as ET print(f"{ET.__file__}") et = ET.parse("/tmp/pom.xml") print(et)
输出:
/usr/local/Cellar/python@3.9/3.9.1_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/xml/etree/ElementTree.py <xml.etree.ElementTree.ElementTree object at 0x10fef9a30>
尝试在ElementTree.py的XMLParser.__init__方法中设置断点,但断点完全未触发。随后在ElementTree.parse方法内添加断点:
def parse(self, source, parser=None): ... close_source = False if not hasattr(source, "read"): source = open(source, "rb") close_source = True try: if parser is None: # If no parser was specified, create a default XMLParser import pdb; pdb.set_trace() parser = XMLParser() if hasattr(parser, '_parse_whole'):
进入pdb交互后,尝试单步进入XMLParser()会直接跳到下一行。用inspect确认模块路径正确:
(Pdb) import inspect (Pdb) inspect.getmodule(XMLParser) <module 'xml.etree.ElementTree' from '/usr/local/Cellar/python@3.9/3.9.1_3/Frameworks/Python.framework/Versions/3.9/lib/python3.9/xml/etree/ElementTree.py'>
以上操作是为了排查自定义XMLParser子类的_start和_end方法未被调用的问题,子类代码如下:
class LineNumberingParser(ET.XMLParser): def _start(self, *args, **kwargs): element = super()._start(*args, **kwargs) element._start_line_number = self.parser.CurrentLineNumber print(f"----- {element.tag} {element._start_line_number}") return element def _end(self, *args, **kwargs): element = super()._end(*args, **kwargs) element._end_line_number = self.parser.CurrentLineNumber print(f"----- {element.tag} {element._end_line_number}") return element parser = LineNumberingParser(target=ET.TreeBuilder(insert_comments=True)) et = ET.parse("/tmp/pom.xml", parser)
尝试给子类添加构造函数并单步进入父类构造方法,同样出现跳过行为,但能看到实例属性初始化正常(如self.target从None变为已初始化状态)。
排查更新
- 在XMLParser的
__init__和_start等方法中添加print语句,完全无输出,说明实际运行的并非Python版本的实现,尽管inspect显示路径正确。 - 发现ElementTree.py模块末尾的代码:
# Import the C accelerators try: # Element is going to be shadowed by the C implementation. We need to keep # the Python version of it accessible for some "creative" by external code # (see tests) _Element_Py = Element # Element, SubElement, ParseError, TreeBuilder, XMLParser, _set_factories from _elementtree import * from _elementtree import _set_factories except ImportError: pass else: _set_factories(Comment, ProcessingInstruction)
推测实际使用的是C原生实现,这就是pdb无法单步调试的原因。目前需要重新寻找获取XML元素行号的解决方案。
3. 找到测试模块中禁用C加速的逻辑,使用纯Python实现时,子类的_start和_end方法会被调用,但代码执行路径与C实现存在明显差异。
内容的提问来源于stack exchange,提问作者haridsv
相关产品推荐
相关产品推荐

