使用 pythonspect 查看函数中的局部变量

发布于 2024-12-24 18:07:41 字数 2586 浏览 6 评论 0原文

我不知道如何使用 inspect / inspect_shell

我猜它涉及使用 getinnerframe 和 getouterframe 遍历框架层次结构,但我对几个问题感到困惑。

鉴于这个例子 nine.py:

import inspect_shell
import time

def number_nine():
    x = 9
    while x==9:
        time.sleep(1)

number_nine()
print x

我想检查 x 的值,甚至可能更改它以使函数返回并打印新值。

首先,我启动 nine.py,然后在单独的命令窗口中,使用 inspect_shell,我看到 < code>getinnerframes 不适用于当前帧(它需要跟踪(也许?))并且当前帧没有“跟踪”。和 getouterframes (如果我向后思考这个问题)似乎只得到与我的功能无关的帧。

>> Inspect Shell v1.0
>> https://github.com/amoffat/Inspect-Shell

localhost:1234> import inspect

localhost:1234> f = inspect.currentframe()

localhost:1234> inspect.getinnerframes(f)
Traceback (most recent call last):
  File "C:\Users\Paul\Desktop\inspect_shell.py", line 143, in run_repl
    try: exec compile(data, "<dummy>", "single") in f_globals, f_globals
  File "<dummy>", line 1, in <module>
  File "C:\Python26\lib\inspect.py", line 942, in getinnerframes
    framelist.append((tb.tb_frame,) + getframeinfo(tb, context))
AttributeError: 'frame' object has no attribute 'tb_frame'


localhost:1234> dir(f)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__',
'__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'f_back', 'f_builtins', 
'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_glo
bals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace']

localhost:1234> print f.f_trace
None

localhost:1234> inspect.getouterframes(f)
[(<frame object at 0x0237D470>, '<dummy>', 1, '<module>', None, None), 
(<frame object at 0x02375938>, 'C:\\Users\\Paul\\Desktop\\inspect_shell.py', 14
3, 'run_repl', ['                    try: exec compile(data, "<dummy>", "single") in
f_globals, f_globals\n'], 0), (<frame object at 0x023A2C30>, 'C:\
\Python26\\lib\\threading.py', 484, 'run', 
['                self.__target(*self.__args, **self.__kwargs)\n'], 0), 
(<frame object at 0x02397F28>, 'C:\
\Python26\\lib\\threading.py', 532, '__bootstrap_inner', 
['                self.run()\n'], 0), (<frame object at 0x023A9D68>, 
'C:\\Python26\\lib\\thre
ading.py', 504, '__bootstrap', ['            self.__bootstrap_inner()\n'], 0)]

I can't figure out how to inspect a currently executing function using inspect / inspect_shell

I'm guessing it involves walking the frame hierarchy using getinnerframe and getouterframe, but I'm confused on several issues.

Given this example
nine.py:

import inspect_shell
import time

def number_nine():
    x = 9
    while x==9:
        time.sleep(1)

number_nine()
print x

I would like to inspect the value of x or even possibly change it to cause the function to return and print the new value.

First I launch nine.py, then in a separate command window, using inspect_shell, I see that getinnerframes does not work on the current frame (it needs a trace (perhaps?)) and the current frame has no "trace". and getouterframes (in case I'm thinking about this backwards) seems to get only frames that have nothing to do with my function.

>> Inspect Shell v1.0
>> https://github.com/amoffat/Inspect-Shell

localhost:1234> import inspect

localhost:1234> f = inspect.currentframe()

localhost:1234> inspect.getinnerframes(f)
Traceback (most recent call last):
  File "C:\Users\Paul\Desktop\inspect_shell.py", line 143, in run_repl
    try: exec compile(data, "<dummy>", "single") in f_globals, f_globals
  File "<dummy>", line 1, in <module>
  File "C:\Python26\lib\inspect.py", line 942, in getinnerframes
    framelist.append((tb.tb_frame,) + getframeinfo(tb, context))
AttributeError: 'frame' object has no attribute 'tb_frame'


localhost:1234> dir(f)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__',
'__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'f_back', 'f_builtins', 
'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_glo
bals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace']

localhost:1234> print f.f_trace
None

localhost:1234> inspect.getouterframes(f)
[(<frame object at 0x0237D470>, '<dummy>', 1, '<module>', None, None), 
(<frame object at 0x02375938>, 'C:\\Users\\Paul\\Desktop\\inspect_shell.py', 14
3, 'run_repl', ['                    try: exec compile(data, "<dummy>", "single") in
f_globals, f_globals\n'], 0), (<frame object at 0x023A2C30>, 'C:\
\Python26\\lib\\threading.py', 484, 'run', 
['                self.__target(*self.__args, **self.__kwargs)\n'], 0), 
(<frame object at 0x02397F28>, 'C:\
\Python26\\lib\\threading.py', 532, '__bootstrap_inner', 
['                self.run()\n'], 0), (<frame object at 0x023A9D68>, 
'C:\\Python26\\lib\\thre
ading.py', 504, '__bootstrap', ['            self.__bootstrap_inner()\n'], 0)]

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

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

发布评论

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

评论(2

揪着可爱 2024-12-31 18:07:41

这有点棘手,但是给定源代码(或一个好的Python反编译器),你可以这样做:

在'nine.py'中:

def number_nine():
    x = 9
    if x == 9:
        print 'x is Nine!'
    else:
        print 'x:', x

你的邪恶代码:

from nine import number_nine

我们需要使用ast,即抽象语法树

import inspect
import ast

现在我们获取源代码并将其转换为 ast:

# Assuming you have the source, we can generate AST from it
nine_src = inspect.getsource(number_nine)
nine_ast = ast.parse(nine_src)

隔离要更改的特定语句:

# This is the Assign object, which represents the 'x = 9' line
# Try to run it interactivly and see how it looks...
x_assign = nine_ast.body[0].body[0]

# Prints 'x'
print x_assign.targets[0].id
# Prints 9
print x_assign.value.n

并且改变它如您所见:

# Change the value of x
# Notice, that we change the assignment itself, a.k.a `x = 9` is now `x = "It's a trap!"`
x_assign.value.n = "It's a trap!"

现在剩下要做的就是将修改后的 ast 对象编译为更有用的东西:

# Compile the new function
new_nine = compile(nine_ast, 'new_nine', 'exec')

您可以使用简单的 exec (如果它在 中,它将替换 'number_nine' globals),或 exec in,并将其放入临时模块中:

# Now we need to execute our litle new_nine (which is a code object)
# This to create the modified version in 'm'
import types
m = types.ModuleType('m', 'The m module')
exec new_nine in m.__dict__
m.number_nine()

# Or this to create it in the global scope
exec new_nine
number_nine()

然后砰!它打印 x: It's a trap!

This is kind of tricky, but given the source code (or a good python decompiler), you can do this:

in 'nine.py':

def number_nine():
    x = 9
    if x == 9:
        print 'x is Nine!'
    else:
        print 'x:', x

Your evil code:

from nine import number_nine

We need to use ast, which is Abstract Syntax Trees:

import inspect
import ast

Now we get the source and transform it to an ast:

# Assuming you have the source, we can generate AST from it
nine_src = inspect.getsource(number_nine)
nine_ast = ast.parse(nine_src)

Isolate the specific statement you want to change:

# This is the Assign object, which represents the 'x = 9' line
# Try to run it interactivly and see how it looks...
x_assign = nine_ast.body[0].body[0]

# Prints 'x'
print x_assign.targets[0].id
# Prints 9
print x_assign.value.n

And alter it as you see fit:

# Change the value of x
# Notice, that we change the assignment itself, a.k.a `x = 9` is now `x = "It's a trap!"`
x_assign.value.n = "It's a trap!"

Now all that is left to do is to compile your modified ast object to something more useful:

# Compile the new function
new_nine = compile(nine_ast, 'new_nine', 'exec')

You can use simple exec (which will replace 'number_nine' if it is in globals), or exec in, and place it in a temp module:

# Now we need to execute our litle new_nine (which is a code object)
# This to create the modified version in 'm'
import types
m = types.ModuleType('m', 'The m module')
exec new_nine in m.__dict__
m.number_nine()

# Or this to create it in the global scope
exec new_nine
number_nine()

And bang! it prints x: It's a trap!

清风挽心 2024-12-31 18:07:41

Inspect Shell 的作者在这里 :) 您可能需要使 x 可从全局命名空间访问。 Inspect Shell 本质上会将您带入正在运行的脚本的全局命名空间,因此如果无法从那里访问某些数据,那么访问它将会非常困难。

所以真正的建议是,使 x 全局化,然后您应该能够更改它并且您的 number_nine() 函数将返回。

希望有帮助!

Author of Inspect Shell here :) You're probably going to need to make x accessible from the global namespace. Inspect Shell essentially drops you into the global namespace of the running script, so if some data can't be reached from there, it's going to be really tricky to get to it.

So really the suggestion is, make x global, then you should be able to alter it and your number_nine() function will return.

Hope that helps!

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