如何在模块中以文本方式查找导入的名称

ash*_*bar 1 python regex python-2.7

我编写了一个名为 that 的方法buildRegex,给定名称(类型为str),返回一个在模块中regex查找语句的对象。from ... import ... namePython

例如,以下是预期行为buildRegex:

>>> regObj = buildRegex('foo')
>>> regObj.search('from a import fool') is None
True
>>> regObj.search('from a import foo') is not None
True
>>> regObj.search('from a.b.c import foo as food') is None
True
>>> regObj.search('from a.b.c import fool, bar as foo') is not None
True
Run Code Online (Sandbox Code Playgroud)

到目前为止,我所掌握的内容适用于上面的所有示例(以及更多):

def buildRegex(name):
    singleImportedName = r'(\b{0}\b(?!\s+as\s+))'.format(name)
    importStatement = r'from\s+(\w+(\.\w+)*)\s+import\s+([^#\n]*)(?={0})'.format(singleImportedName )
    return re.compile(importStatement)
Run Code Online (Sandbox Code Playgroud)

buildRegex假设搜索到的模块没有SyntaxErrors,这是可以的。

我的问题是,在查找导入的名称时foo,我还需要知道它是否是其他名称的别名。即如果一个模块有这样的语句:

from a.b.c import bar as foo
Run Code Online (Sandbox Code Playgroud)

我想知道什么foo是别名,在这种情况下,那就是bar. 目前,由于asserted lookaheads在正则表达式中,这是不可能的。所以,最后我的问题是:如何重构正则表达式以便这些信息不会丢失,即,如果给定的名称是别名,那么其别名的名称位于其中一个regex组中?

Ant*_*ala 5

我建议不要编写复杂的正则表达式来解析导入,而是实际上使用将ast.parse源代码解析为抽象语法树并从那里找到名称,这样ast.parse可以保证正确解析Python。就像是:

import ast

class ImportFinder(ast.NodeVisitor):
    def __init__(self):
        self.imports = []

    def visit_Import(self, node):
        names = []
        for i in node.names:
            names.append((i.name, i.asname))
        self.imports.append(['import', names])

    def visit_ImportFrom(self, node):
        module = node.module
        level = node.level  # how many dots
        names = []
        for i in node.names:
            names.append((i.name, i.asname))

        self.imports.append(('from', level, module, names))

def parse_imports(source):
    tree = ast.parse(source)
    finder = ImportFinder()
    finder.visit(tree)
    return finder.imports
Run Code Online (Sandbox Code Playgroud)

用法示例:

import pprint

pprint.pprint(parse_imports('''
from foo import bar, baz, frob
from .. import bar as spam, baz as ham, frob
import bar.baz
import bar.foo as baf
'''))
Run Code Online (Sandbox Code Playgroud)

打印出:

[('from', 0, 'foo', [('bar', None), ('baz', None), ('frob', None)]),
 ('from', 2, None, [('bar', 'spam'), ('baz', 'ham'), ('frob', None)]),
 ['import', [('bar.baz', None)]],
 ['import', [('bar.foo', 'baf')]]]
Run Code Online (Sandbox Code Playgroud)

行上的整数给出了模块名称之前from的编号。.