exec代码在原始的本地框架范围内

时间:2010-08-04 16:43:47

标签: python debugging exec frame

我编写了一个远程Python调试器,我需要的一个功能是在断点处停止时执行任意代码。我的调试器使用以下命令执行从远程调试器接收的代码:

exec (compile(code, '<string>', 'single') , frame.f_globals, frame.f_locals)

这在大多数情况下都可以正常工作,但我注意到了一些问题。

  1. 分配语句实际上并未应用于原始本地词典。这可能是因为f_locals应该是只读的。

  2. 如果在类方法中停止,则访问受保护的属性(以双下划线开头的名称)不起作用。我假设这是由于Python对受保护属性执行的名称修改。

  3. 所以我的问题是,有没有解决这些限制的方法?我可以欺骗Python认为代码是在该框架的实际局部范围内执行的吗?

    我正在使用CPython 2.7,我愿意接受特定于此版本的解决方案/黑客。

3 个答案:

答案 0 :(得分:2)

  

作业陈述实际上并非如此   适用于原始的当地人   字典。这可能是由于   事实上f_locals应该是   是只读的。

不完全是,但函数的字节码不会查看locals,而是使用简单但关键的优化,即局部变量在一个简单的数组中,避免运行时查找。避免这种情况的唯一方法(并使函数更慢)正在编译不同的代码,例如:以exec ''开头的代码强制编译器避免优化(在Python 2中;没有办法,在Python 3中)。如果你需要使用现有的字节码,那你就不走运了:没有的方式来实现你想要的。

  

如果在类方法中停止,   访问受保护的属性(名称   从双下划线开始)   不行。我假设这是由于   Python执行的名称错误   关于受保护的属性。

是的,所以这个问题 允许一种解决方法:在名称前加_Classname来模仿编译器的作用。请注意,双下划线前缀表示私有受保护将是下划线(并且不会给您带来任何麻烦)。私有名称专门用于避免在子类中绑定名称的意外类(并且为了这一目的而正常工作,但不是完美的,而不是其他任何东西; - )。

答案 1 :(得分:0)

我不确定我是否理解正确,但exec会在代码中使用分配填充locals参数:

>>> loc = {}
>>> exec(compile('a=3', '<string>', 'single'), {}, loc)
>>> loc
{'a': 3}

也许f_locals不允许写入。

答案 2 :(得分:0)

  

在断点处停止时执行任意代码...我可以欺骗Python认为代码是在该帧的实际本地范围内执行的吗?

Python调试器pdb允许这样做。例如,假设您正在调试文件tests/scopeTest.py,并且您的程序中有以下行,其中变量尚未在程序中声明:

print (NOT_DEFINED_IN_PROGRAM)

以便运行代码python tests/scopeTest.py会导致:

NameError: name 'NOT_DEFINED_IN_PROGRAM' is not defined

现在,您希望在调试器中的该行停止时定义该变量,并让程序继续执行,使用该变量,就好像它一直在程序中定义一样。换句话说,您希望在该范围内实现更改,以便您可以继续执行该更改。它实际上是可能的:

$ python -m pdb tests/scopeTest.py
> /home/user/tests/scopeTest.py(1)<module>()
-> print (NOT_DEFINED_IN_PROGRAM)
(Pdb) 'NOT_DEFINED_IN_PROGRAM' in locals()
False
(Pdb) NOT_DEFINED_IN_PROGRAM = 5
(Pdb) 'NOT_DEFINED_IN_PROGRAM' in locals()
True
(Pdb) step
5

Pdb通过其compile函数中的execdefault执行此操作,其功能相当于:

code = compile(line + '\n', <stdin>, 'single')
exec(code, self.curframe.f_globals, self.curframe_locals)

其中self.curframe是特定的框架。现在,self.curframe_locals不是self.curframe.f_locals,因为setup函数说:

# The f_locals dictionary is updated from the actual frame
# locals whenever the .f_locals accessor is called, so we
# cache it here to ensure that modifications are not overwritten.
self.curframe_locals = self.curframe.f_locals

希望有所帮助,这就是你的意思!

请注意,即便如此,您是否应该例如使用猴子修补版本替换正在调试的程序上下文中的函数,例如:

newGlobals['abs'] = myCustomAbsFunction
exec(code, newGlobals, locals)

myCustomAbsFunction的范围不是用户程序,而是将定义该函数的上下文,即调试器!也有一种解决方法,但由于没有特别要求,现在它仍然是读者的练习。 ^ __ ^