如何从 ast.Call 对象获取函数结果?

How to get the function result from ast.Call object?

我需要在没有 运行 文件的情况下测试 python 模块中的 python 字典。所以我决定用 ast.parse 解析它。我几乎弄清楚了如何构建原始字典,只是我找不到让函数值起作用的方法。

config.py
...
config = {
    # theese None, booleans and strings I got parsed
    user_info: None,
    debug: False,
    cert_pass: "test",

    # the function values I have problem with
    project_directory: os.path.join(__file__, 'project')
}
...


test.py
...
# Skiping the AST parsing part, going straight to parsing config dict 

def parse_ast_dict(ast_dict):
   #ast_dict is instance of ast.Dict
   result_dict = {}
   # iterating over keys and values
   for item in zip(ast_dict.keys, ast_dict.values):
       if isinstance(item[1], ast.NameConstant):
           result_dict[item[0].s] = item[1].value
       elif isinstance(item[1], ast.Str):
           result_dict[item[0].s] = item[1].s
       elif isinstance(item[1], ast.Num):
           result_dict[item[0].s] = item[1].n

       # I got stuck here parsing the ast.Call which in my case is os.path.join calls
       elif isinstance(item[0].s, ast.Call):
           pass
   return result_dict

我无法将配置对象移动到不同的文件,以便我可以单独测试它,因为它是供应商提供的代码片段,也无法将其导入测试,因为它包含大量库导入所以我坚持 ast.

要评估一个函数调用,您需要实际执行它(除非您想自己实现一个 Python 解释器)。

但是,为了避免执行整个 config.py,您应该专注于从 AST 中提取所需的字典节点以进行独立评估。

为此,首先找到赋值目标为 'config' 的赋值节点(因为这是接收字典的变量的名称)。然后,提取赋值节点的值,在本例中就是你想要的字典。从值构建一个 Expression 节点,用 ast.fix_missing_locations 调整行号和代码偏移量,以 'eval' 模式编译它,最后用 eval 评估编译后的代码对象它将 return 您要查找的词典。请记住向 eval 传递一个全局字典,其中包含 os__file__ 等必要名称的适当值,以便可以正确调用其中的函数。

import ast
import os

config_path = 'config.py'
with open(config_path) as config_file:
    for node in ast.walk(ast.parse(config_file.read())):
        if isinstance(node, ast.Assign) and node.targets[0].id == 'config':
            expr = ast.Expression(body=node.value)
            ast.fix_missing_locations(expr)
            config = eval(
                compile(expr, '', 'eval'),
                {'os': os, '__file__': os.path.realpath(config_path)}
            )
            break

print(config)

演示:https://replit.com/@blhsing/BluevioletMeaslyFlash