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

发布于 2024-09-13 01:40:45 字数 488 浏览 4 评论 0原文

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

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

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

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

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

所以我的问题是,有没有办法绕过这些限制?我可以欺骗 Python 让它认为代码是在该帧的实际本地范围内执行的吗?

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

I've written a remote Python debugger and one of the features I need is to execute arbitrary code while stopped at a breakpoint. My debugger uses the following to execute code received from the remote debugger:

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

This works fine for the most part, but I've noticed a couple issues.

  1. Assignment statements aren't actually applied to the original locals dictionary. This is probably due to the fact that f_locals is supposed to be read-only.

  2. If stopped within a class method, accessing protected attributes (names beginning with double underscore) does not work. I'm assuming this is due to the name mangling that Python performs on protected attributes.

So my question is, is there a way around these limitations? Can I trick Python into thinking that the code is being executed in the actual local scope of that frame?

I'm using CPython 2.7, and I'm willing to accept a solution/hack specific to this version.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

回忆追雨的时光 2024-09-20 01:40:45

赋值语句实际上并不是
适用于原来的当地人
字典。这可能是由于
事实上 f_locals 应该
为只读。

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

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

是的,所以这个问题确实允许一种解决方法:在名称前面添加_Classname来模仿编译器的行为。请注意,双下划线前缀意味着 privateprotected 将是下划线(并且不会给您带来任何麻烦)。私有名称专门用于避免名称绑定在子类中的意外类(并且可以很好地用于该目的,尽管并不完美,并且不能用于其他任何目的;-)。

Assignment statements aren't actually
applied to the original locals
dictionary. This is probably due to
the fact that f_locals is supposed to
be read-only.

Not exactly, but the bytecode for the function will not look at locals, using rather a simple but crucial optimization whereby local variables are in a simple array, avoiding runtime lookups. The only way to avoid this (and make the function much, much slower) is compiling different code, e.g. code starting with an exec '' to force the compiler to avoid the optimization (in Python 2; no way, in Python 3). If you need to work with existing bytecode, you're out of luck: there is no way to accomplish what you desire.

If stopped within a class method,
accessing protected attributes (names
beginning with double underscore) does
not work. I'm assuming this is due to
the name mangling that Python performs
on protected attributes.

Yep, so this issue does allow a workaround: prepend _Classname to the name to mimic what the compiler does. Note that double-underscore prefixes means private: protected would be a single underscore (and would give you no trouble). Private names are specifically meant to avoid accidental classes with names bound in subclasses (and work decently for that one purpose, though not perfectly, and not for anything else;-).

梅倚清风 2024-09-20 01:40:45

我不确定我是否正确理解了您的意思,但是 exec 确实使用代码内的赋值填充了 locals 参数:

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

也许 f_locals 没有不允许写入。

I'm not sure I've understood you correctly, but exec does populate the locals parameter with assignments inside the code:

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

Perhaps f_locals doesn't allow writes.

梦里人 2024-09-20 01:40:45

在断点处停止时执行任意代码...我可以欺骗 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 通过其 default 函数中的 compileexec 来完成此操作,这相当于:

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

where self .curframe 是一个特定的框架。现在,self.curframe_locals 不是 self.curframe.f_locals,因为,正如 setup 函数所述:

# f_locals字典是根据实际帧更新的
# 每当调用 .f_locals 访问器时都会调用 locals,所以我们
# 将其缓存在这里以确保修改不会被覆盖。
self.curframe_locals = self.curframe.f_locals

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

请注意,即使如此,您是否希望将正在调试的程序上下文中的函数替换为经过猴子修补的版本,例如:

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

myCustomAbsFunction 的范围不是将是用户程序,但将是定义该函数的上下文,即调试器!也有一种解决方法,但由于没有具体询问,因此目前将其作为练习留给读者。 ^__^

to execute arbitrary code while stopped at a breakpoint ... Can I trick Python into thinking that the code is being executed in the actual local scope of that frame?

The Python debugger, pdb, allows this. For example, let's say you are debugging the file tests/scopeTest.py, and you have the following line in your program, where the variable hasn't been declared in the program itself :

print (NOT_DEFINED_IN_PROGRAM)

so that running the code python tests/scopeTest.py would result in :

NameError: name 'NOT_DEFINED_IN_PROGRAM' is not defined

Now you would like to define that variable when stopped at that line in the debugger, and have the program continue executing, using that variable as if it had been defined in the program all along. In other words, you would like to effect the change within that scope, so that you can continue execution with that change permanent. It is actually possible :

$ 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 does this through a compile and exec in its default function, which does the equivalent of :

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

where self.curframe is a specific frame. Now, self.curframe_locals is not self.curframe.f_locals, because, as the setup function says :

# 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

Hope that helps, and is what you meant!

Take note that, even then, should you want to, for example, replace a function in the context of the program being debugged with a monkey-patched version, such as:

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

the scope of the myCustomAbsFunction is not going to be the user program, but is going to be the context of where that function was defined, which is the debugger! There is a way around that too, but as it wasn't specifically asked, it is left as an exercise for the reader, for now. ^__^

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文