Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/297.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 我可以在抽象语法树中处理导入吗?_Python_Python 2.7_Abstract Syntax Tree - Fatal编程技术网

Python 我可以在抽象语法树中处理导入吗?

Python 我可以在抽象语法树中处理导入吗?,python,python-2.7,abstract-syntax-tree,Python,Python 2.7,Abstract Syntax Tree,我想解析并检查config.py中允许的节点。 config.py可以导入其他配置文件,也必须检查这些文件 ast模块中是否有任何功能可以解析ast.Import和ast.ImportFrom对象到ast.module对象 下面是一个代码示例,我正在检查一个配置文件(path\u to\u config),但我还想检查它导入的任何文件: with open(path_to_config) as config_file: ast_tree = ast.parse(config_file.r

我想解析并检查
config.py
中允许的节点。
config.py
可以导入其他配置文件,也必须检查这些文件

ast
模块中是否有任何功能可以解析
ast.Import
ast.ImportFrom
对象到
ast.module
对象

下面是一个代码示例,我正在检查一个配置文件(
path\u to\u config
),但我还想检查它导入的任何文件:

with open(path_to_config) as config_file:
    ast_tree = ast.parse(config_file.read())
    for script_object in ast_tree.body:
        if isinstance(script_object, ast.Import):
            # Imported file must be checked too
        elif isinstance(script_object, ast.ImportFrom):
            # Imported file must be checked too
        elif not _is_admissible_node(script_object):
            raise Exception("Config file '%s' contains unacceptable statements" % path_to_config)

这比你想象的要复杂一点
from foo import name
是导入
foo
模块中定义的对象和
foo.name
模块中定义的对象的有效方法,因此您可能必须尝试这两种形式,以查看它们是否解析为文件。Python还允许别名,其中代码可以导入
foo.bar
,但实际模块实际上定义为
foo.\u bar\u实现
,并作为
foo
包的属性提供。仅通过查看
Import
ImportFrom
节点,无法检测所有这些情况

如果忽略这些情况,只查看来自名称的
,那么对于每次导入,仍然必须将模块名称转换为文件名,然后从文件中解析源代码

在Python2中,可以使用获取模块(*)的打开文件对象。在解析每个模块时,您希望保留完整的模块名称,因为您需要它来帮助您在以后确定包的相对导入
imp.find_module()
无法处理包导入,因此我创建了一个包装函数:

import imp

_package_paths = {}
def find_module(module):
    # imp.find_module can't handle package paths, so we need to do this ourselves
    # returns an open file object, the filename, and a flag indicating if this
    # is a package directory with __init__.py file.
    path = None
    if '.' in module:
        # resolve the package path first
        parts = module.split('.')
        module = parts.pop()
        for i, part in enumerate(parts, 1):
            name = '.'.join(parts[:i])
            if name in _package_paths:
                path = [_package_paths[name]]
            else:
                _, filename, (_, _, type_) = imp.find_module(part, path)
                if type_ is not imp.PKG_DIRECTORY:
                    # no Python source code for this package, abort search
                    return None, None
                _package_paths[name] = filename
                path = [filename]
    source, filename, (_, _, type_) = imp.find_module(module, path)
    is_package = False
    if type_ is imp.PKG_DIRECTORY:
        # load __init__ file in package
        source, filename, (_, _, type_) = imp.find_module('__init__', [filename])
        is_package = True
    if type_ is not imp.PY_SOURCE:
        return None, None, False
    return source, filename, is_package
我还将跟踪您已经导入的模块名称,这样您就不会对它们进行两次处理;使用
spec
对象中的名称确保跟踪其规范名称

使用堆栈处理所有模块:

with open(path_to_config) as config_file:
    # stack consists of (modulename, ast) tuples
    stack = [('', ast.parse(config_file.read()))]

seen = {}
while stack:
    modulename, ast_tree = stack.pop()
    for script_object in ast_tree.body:
        if isinstance(script_object, (ast.Import, ast.ImportFrom)):
            names = [a.name for a in script_object.names]
            from_names = []
            if hasattr(script_object, 'level'):  # ImportFrom
                from_names = names
                name = script_object.module
                if script_object.level:
                    package = modulename.rsplit('.', script_object.level - 1)[0]
                    if script_object.module:
                        name = "{}.{}".format(name, script_object.module)
                    else:
                        name = package
                names = [name]
            for name in names:
                if name in seen:
                    continue
                seen.add(name)
                source, filename, is_package = find_module(name)
                if source is None:
                    continue
                if is_package and from_names:
                    # importing from a package, assume the imported names
                    # are modules
                    names += ('{}.{}'.format(name, fn) for fn in from_names)
                    continue
                with source:
                    module_ast = ast.parse(source.read(), filename)
                queue.append((name, module_ast))

        elif not _is_admissible_node(script_object):
            raise Exception("Config file '%s' contains unacceptable statements" % path_to_config)
如果
从foo导入bar
导入,如果
foo
是一个包,则跳过
foo/\uu init\uuuuuuuuuuupy
,并假定
bar
将是一个模块



(*)
imp.find_module()
不推荐用于Python 3代码。在Python3上,您将使用获取模块加载器规范,然后使用获取文件名
importlib.util.find_spec()
知道如何处理包。

看看问题是我不知道如何导入ast.import obj with ast lib:(我有一个“ast.import”实例,但我不知道如何解析它。你想用它做什么?查找源文件位置(它并不总是显示,例如对于
C
扩展名或动态创建的模块)?或导入它(可能会导致不希望的副作用,如删除文件)?配置文件可以导入另一个配置文件。我需要检查配置是否包含可接受的节点。(在我的示例中,可接受的是ast.import、ast.ImportFrom、ast.Assign、ast.if)但是配置文件可能会导入另一个配置文件。我需要检查导入的文件是否包含可接受的节点。等等。在我的示例中,我只能检查一个文件“path_to_config”,但我想检查main cfg也导入的文件。也许有其他库用于这些任务?谢谢,伙计,你帮了我很大的忙。