如何执行嵌套的PyCode对象

时间:2019-03-14 12:33:40

标签: python

假设我们有一个这样的代码对象:

code = '''
x = 'unrelated'
y = dangerous_function()

def foo():
    return 'say foo'
'''
code_obj = compile(code, '<string>', 'exec')

我不想仅仅执行它,因为谁知道会发生什么(特别是dangerous_function调用看起来很杂乱)。但是,我愿意用其中定义的任何函数填充当前作用域,这似乎可以做到:

import types    

new_objects = []
for obj in code_obj.co_consts:
    if isinstance(obj, types.CodeType):
        new_objects.append(obj.co_name)
        print(obj)           # "<code object foo at 0x7f4e255d3150, file "<string>", line 4>"
                             # ... looks promising, so let's exec it!
        exec(obj)

print(new_objects[0])        # "foo"
print(eval(new_objects[0]))  # "NameError: name 'foo' is not defined"

我希望最后一条语句打印say foo而不是引发NameError。这样做的原因必须是exec(obj)并没有执行我期望的操作,即它没有运行在父代码对象中分配给名称foo的代码对象。 / p>

有办法吗?

1 个答案:

答案 0 :(得分:1)

co_const属性仅包含在代码对象中定义的常量文字,因此在您的示例中,该属性仅包含加载'say foo'作为返回参数的代码,可以使用{{ 1}}:

dis

这将输出:

import dis
for obj in code_obj.co_consts:
    if isinstance(obj, types.CodeType):
        dis.dis(obj)

因此,通过执行此代码对象,自然不会定义任何名称。

如果您只想在给定的源代码中执行特定功能,则可以使用 5 0 LOAD_CONST 1 ('say foo') 2 RETURN_VALUE 解析代码,并使用ast.parse子类提取功能节点,并用{包裹{1}}节点,因此您可以单独编译和执行它:

ast.NodeVisitor

这将输出:

Module

编辑:或者更简单地,您可以使用import ast class get_function(ast.NodeVisitor): def __init__(self, name): self.name = name self.code = None def visit_FunctionDef(self, node): if node.name == self.name: self.code = compile(ast.fix_missing_locations(ast.Module(body=[node])), '<string>', 'exec') func = get_function('foo') func.visit(ast.parse(code, '<string>')) exec(func.code) print(eval('foo')) 函数通过<function foo at 0x018735D0> 循环遍历节点:

ast.walk