【发布时间】:2023-01-11 20:43:14
【问题描述】:
我写了一个“编译器”PypTeX,它将包含Hello @{3+4} 的输入文件a.tex 转换为包含Hello 7 的输出文件a.pyptex。我使用 eval(compile('3+4','a.tex',mode='eval'),myglobals) 之类的东西评估任意 Python 片段,例如 @{3+4},其中 myglobals 是一些(最初为空的)字典。这为在a.tex 中运行代码创建了一个嵌入式解释器的幻觉,但是运行'3+4' 时的调用堆栈看起来很奇怪,因为它一直备份到 PypTeX 解释器,而不是在用户代码处达到顶峰'3+4'在a.tex。
有没有办法做类似eval但切断调用堆栈顶部的事情?
动机:调试
想象一下,在 numpy 深处的 Python 片段引发了一个异常,并且启动了 pdb。用户输入up,直到达到用户代码的范围,然后输入list。我这样做的方式显示了 a.tex 文件,这是向用户显示的正确上下文,也是我这样做的原因。然而,如果用户再次输入up,用户最终会进入 PypTeX 编译器的内部。
一个类比是,如果 g++ 编译器在模板中有一个错误,在其错误消息中显示模板“调用堆栈”,但该模板调用堆栈一直返回到实际 g++ 调用堆栈的内部,并且暴露了只会让用户感到困惑的内部 g++ 细节。
在 Python 中嵌入 Python
也许问题在于eval创建的“嵌入式解释器”的错觉有点太薄了。 eval 允许指定全局变量,但它会继承调用者拥有的任何调用堆栈,因此如果可以以某种方式为 eval 提供截断的调用堆栈,那将解决我的问题。或者,如果 pdb 可以被告知“你不能再往上走”超过某个堆栈帧,那也会有所帮助。例如,如果我可以在回溯对象中切掉一部分堆栈,然后将其传递给pdb.post_mortem()。
或者,如果有人可以做 from sys import Interpreter; foo = Interpreter(); foo.eval(...),这意味着 foo 是一个干净的嵌入式解释器,具有独特的调用堆栈、全局变量等……,那也很好。
有没有办法做到这一点?
一个被拒绝的选择
一种不好的方法是通过正则表达式从 a.tex 中提取所有 Python 片段,将它们转储到临时文件 a.py 中,然后通过在命令行调用全新的 Python 解释器来运行它们。这导致pdb最终达到a.py。我试过了,这是一个非常糟糕的用户体验。 a.py 应该是一个实现细节;它是自动生成的,用户看起来会很陌生。用户很难弄清楚a.py的哪些位来自a.tex的哪些位。对于大文档,我发现这太难用了。另见pythontex。
【问题讨论】:
-
这不是真正的答案(这就是为什么它是评论)但是你有没有研究过使用
skipparameter 创建的pdb实例的可能性,它可以让你列出调试器不会进入的模块? -
感谢这个,我没有注意到
skip参数,你是对的,它可能会稍微改善嵌入的错觉。另外,我现在已经注意到jinja2.debug,这可能也是相关的。 -
嗯,根据this,内置回溯类型的对象现在可以在某种程度上被构造和改变。这可能就是我所需要的。
标签: python compiler-construction introspection