【问题标题】:Embed Python in Python?在 Python 中嵌入 Python?
【发布时间】: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

【问题讨论】:

  • 这不是真正的答案(这就是为什么它是评论)但是你有没有研究过使用 skip parameter 创建的 pdb 实例的可能性,它可以让你列出调试器不会进入的模块?
  • 感谢这个,我没有注意到 skip 参数,你是对的,它可能会稍微改善嵌入的错觉。另外,我现在已经注意到jinja2.debug,这可能也是相关的。
  • 嗯,根据this,内置回溯类型的对象现在可以在某种程度上被构造和改变。这可能就是我所需要的。

标签: python compiler-construction introspection


【解决方案1】:

我想我找到了一个足够的解决方案:

import pdb, traceback

def exec_and_catch(cmd,globals):
  try:
    exec(cmd,globals)            # execute a user program
  except Exception as e:
    tb = e.__traceback__.tb_next # if user program raises an exception, remove the
    f = e.with_traceback(tb)     # top stack frame and return the exception.
    return f
  return None                    # otherwise, return None signifying success.

foo = exec_and_catch("import module_that_does_not_exist",{})
if foo is not None:
    traceback.print_exception(value=foo, tb=foo.__traceback__, etype=type(foo))
    pdb.post_mortem(foo.__traceback__)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-12
    • 2011-12-10
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 2020-01-16
    • 2015-10-15
    • 1970-01-01
    相关资源
    最近更新 更多