使用 Python 的 AST 查找多行导入自语句中的第一行



我目前正试图在Python文件中找到所有ast.Importast.ImportFrom节点。但是,在多行import语句中,如

from foo import (
bar,
baz,
coo
)

每个子模块(bar,bazcoo)所提到的行号都在提到它们的行上,而不是原来的from foo import行。我怎样才能得到import语句开始的那一行(对于单行导入,它是一样的)。

或者,是否有一种方法可以迭代地获得一个作用域中的所有导入(遍历脚本中的所有作用域)?

更新:显然,ast.walk不像我想的那样为每行返回一个节点。这实际上是因为我为每个node.names做了一个不同的元组。将其修改为只返回名字(并使用node.lineno)效果很好。不过@rici的回答还是不错的,所以我就不提了。

您可以只使用ast.ImportFrom节点的lineno属性,这是语句开始的行号。(还有一个end_lineno属性,在这种情况下可能不太有用。)

下面是一个小例子:

import ast
sample = '''
# This is line 1
#
# This is line 3
#
# The next line is line 6
from foo import (
bar,
baz,
coo
)
import pkg.mod, other_mod
'''.strip()
class myWalker(ast.NodeVisitor):
def visit_Import(self, node):
print(f"""Line {node.lineno} imports modules {
', '.join(alias.name for alias in node.names)
}""")
def visit_ImportFrom(self, node):
print(f"""Line {node.lineno} imports from module {node.module} the names {
', '.join(alias.name for alias in node.names)
}""")
myWalker().visit(ast.parse(sample, filename='<test>', mode='exec'))

输出:

Line 6 imports from module foo the names bar, baz, coo
Line 11 imports modules pkg.mod, other_mod

要按作用域获取导入名称,我认为您必须遍历语法树,注意函数定义。(其他作用域,如lambda和推导式,在这里不重要,因为它们只允许表达式,不允许语句。但是,如果您想做得正确,还需要跟踪声明为globalnonlocal的名称,因为函数可以使用其中一种声明将导入的名称注入到不同的作用域。

最新更新