获取 C python exec 参数字符串或访问评估堆栈
getting the C python exec argument string or accessing the evaluation stack
在我的 python debugger I have a way of remapping a string to a filename so that when you are stepping through an exec'd function inside the debugger you can list lines pygmentized, or view them along inside an editor like Emacs via realgud.
所以我希望能够在 CPython 停止评估时在 exec 语句中提取字符串。
我已经有了一种机制,可以在调用帧中回顾以查看 caller was an EXEC_STMT
,并且我可以回顾一条指令以查看上一条指令是否为 DUP_TOP
。因此,如果我能想出一种在调用时读取堆栈条目并给出评估的字符串的方法,我就可以自由回家了。可能有一种方法可以进入 C 来获得它,但我缺乏对 CPython 内部知识的了解,并且不想这样做。如果那里有一个包,也许我可以选择包含它。
CPython 已经提供了对函数参数和局部变量的访问,但是当然,因为这是一个内置函数,所以不会将其记录为函数参数。
如果对如何做同样的事情有其他想法,那也没关系。我觉得不太好的解决方案是以某种方式尝试过载或替换 exec
,因为调试器可以在游戏后期引入。
我知道 CPython2 和 CPython3 在这方面可能有点不同,但从任何一个开始都可以。
我想我现在找到了方法。
在调试器内部,我将调用堆栈向上一层到达 exec
语句。然后我可以使用 uncompyle6 来获取源代码的语法树。 (可能需要在 uncompyle6 中进行更改以使其更容易。)
调用点的树会有类似 exec_stmt -> expr ...
的东西。该表达式将具有表达式的 text,这不一定是表达式的值。表达式可以是常量字符串值,但也可以是像 "foo" + var1
.
这样复杂的东西
这样调试器就可以在知道如何计算调用堆栈中的表达式的调试器上下文中计算该字符串。
这仍然存在重新计算表达式可能有副作用的问题。但这是糟糕的编程习惯,对吧? ;-)
因此,如果源代码不存在,我所做的只是从字节码中反编译代码。这有一个缺点,即字节码中提到的行号并不总是与字节码中的行号一致。为此,重新创建上面的字符串的方法更好。
最后,我希望给出一些想法,为什么编写一个真正好的调试器很难,为什么大量的调试器在即使是简单的事情上都有很多限制,比如在您当前停止的时候获取源文本.
一种完全不同的方法是提前停止并切换到像 x-python 这样的子解释器(或一些适当修改的 Python C 模块),它可以访问堆栈。
SO问题的开源Thonny IDE has [sub]expression evaluation stepping. See the Tracing Python expression evaluation step by step.
不完全是答案,但在某些情况下这可能是解决此问题的方法。
您可以提供自己的自定义 exec
函数,该函数扩展 linecache
以包含此函数。许多回溯将包含代码。喜欢:
def custom_exec(code_str, _globals=None, _locals=None):
compile_string_fn = f"<custom code str {hash(code_str)}>"
c = compile(code_str, compile_string_fn, "exec")
set_linecache(compile_string_fn, code_str)
exec(c, _globals, _locals)
与:
def set_linecache(filename, source):
import linecache
linecache.cache[filename] = None, None, [line+'\n' for line in source.splitlines()], filename
在我的 python debugger I have a way of remapping a string to a filename so that when you are stepping through an exec'd function inside the debugger you can list lines pygmentized, or view them along inside an editor like Emacs via realgud.
所以我希望能够在 CPython 停止评估时在 exec 语句中提取字符串。
我已经有了一种机制,可以在调用帧中回顾以查看 caller was an EXEC_STMT
,并且我可以回顾一条指令以查看上一条指令是否为 DUP_TOP
。因此,如果我能想出一种在调用时读取堆栈条目并给出评估的字符串的方法,我就可以自由回家了。可能有一种方法可以进入 C 来获得它,但我缺乏对 CPython 内部知识的了解,并且不想这样做。如果那里有一个包,也许我可以选择包含它。
CPython 已经提供了对函数参数和局部变量的访问,但是当然,因为这是一个内置函数,所以不会将其记录为函数参数。
如果对如何做同样的事情有其他想法,那也没关系。我觉得不太好的解决方案是以某种方式尝试过载或替换 exec
,因为调试器可以在游戏后期引入。
我知道 CPython2 和 CPython3 在这方面可能有点不同,但从任何一个开始都可以。
我想我现在找到了方法。
在调试器内部,我将调用堆栈向上一层到达 exec
语句。然后我可以使用 uncompyle6 来获取源代码的语法树。 (可能需要在 uncompyle6 中进行更改以使其更容易。)
调用点的树会有类似 exec_stmt -> expr ...
的东西。该表达式将具有表达式的 text,这不一定是表达式的值。表达式可以是常量字符串值,但也可以是像 "foo" + var1
.
这样调试器就可以在知道如何计算调用堆栈中的表达式的调试器上下文中计算该字符串。
这仍然存在重新计算表达式可能有副作用的问题。但这是糟糕的编程习惯,对吧? ;-)
因此,如果源代码不存在,我所做的只是从字节码中反编译代码。这有一个缺点,即字节码中提到的行号并不总是与字节码中的行号一致。为此,重新创建上面的字符串的方法更好。
最后,我希望给出一些想法,为什么编写一个真正好的调试器很难,为什么大量的调试器在即使是简单的事情上都有很多限制,比如在您当前停止的时候获取源文本.
一种完全不同的方法是提前停止并切换到像 x-python 这样的子解释器(或一些适当修改的 Python C 模块),它可以访问堆栈。
SO问题的开源Thonny IDE has [sub]expression evaluation stepping. See the
不完全是答案,但在某些情况下这可能是解决此问题的方法。
您可以提供自己的自定义 exec
函数,该函数扩展 linecache
以包含此函数。许多回溯将包含代码。喜欢:
def custom_exec(code_str, _globals=None, _locals=None):
compile_string_fn = f"<custom code str {hash(code_str)}>"
c = compile(code_str, compile_string_fn, "exec")
set_linecache(compile_string_fn, code_str)
exec(c, _globals, _locals)
与:
def set_linecache(filename, source):
import linecache
linecache.cache[filename] = None, None, [line+'\n' for line in source.splitlines()], filename