如何查找导入的Python模块的行和列偏移量



我有一个类(基于这个答案(,它使用ast.NodeVisitor来获取Python文件导入的模块列表。但是,我还想返回模块名称在文件中的位置的行和列偏移量。

代码:

import ast
class ImportFinder(ast.NodeVisitor):
def __init__(self):
self.imports = []
def visit_Import(self, node):
for i in node.names:
self.imports.append({'import_type': "import", 'module': i.name,})
def visit_ImportFrom(self, node):
self.imports.append({'import_type': "from", 'module': node.module})
def parse_imports(source):
tree = ast.parse(source)
finder = ImportFinder()
finder.visit(tree)
return finder.imports

# Example usage
sample_file = '''
from foo import bar, baz, frob
import bar.baz
import   bar.foo as baf
'''
parsed_imports = parse_imports(sample_file)
for i in parsed_imports:
print(i)

电流输出:

{'import_type': 'from', 'module': 'foo'}
{'import_type': 'import', 'module': 'bar.baz'}
{'import_type': 'import', 'module': 'bar.foo'}

期望输出:

{'import_type': 'from', 'module': 'foo', 'line': 2, 'column_offset': 5}
{'import_type': 'import', 'module': 'bar.baz', 'line': 3, 'column_offset': 7}
{'import_type': 'import', 'module': 'bar.foo', 'line': 4, 'column_offset': 9}

如何获取导入的Python模块名称的行和列偏移量?

您可以将此视为一个起点。它不处理续行,但它将是一个马基雅维利式的程序员,他写道:

import 
os

您可以通过使用filter函数来组合continuation并生成较长的行来处理此问题。

import re
def parse_imports(source):
hits = []
source = re.sub(r"'''[']'''","",source)
source = re.sub(r'"""["]"""',"",source)
for no,line in enumerate(source.splitlines()):
ls = line.lstrip()
if ls.startswith( "from " ):
p1 = ls.split()
mod = p1[1].rstrip()
i1 = line.find(mod)
hits.append({
"import_type": p1[0],
"module": mod,
"line": no+1,
"column_offset": i1
})
elif ls.startswith( "import" ):
cl = ls.split(',')
p1 = cl[0].split()
for mod in  [p1[1]] + [c.strip().split()[0] for c in cl[1:]]:
i1 = line.find(mod)
hits.append({
"import_type": p1[0],
"module": mod,
"line": no+1,
"column_offset": i1
})
return hits
# Example usage
sample_file = '''
from foo import bar, baz, frob
import bar.baz
import   bar.foo as baf
import  os,re,  sys
'''
parsed_imports = parse_imports(sample_file)
for i in parsed_imports:
print(i)

输出:

{'import_type': 'from', 'module': 'foo', 'line': 2, 'column_offset': 5}
{'import_type': 'import', 'module': 'bar.baz', 'line': 3, 'column_offset': 7}
{'import_type': 'import', 'module': 'bar.foo', 'line': 4, 'column_offset': 9}
{'import_type': 'import', 'module': 'os', 'line': 5, 'column_offset': 8}
{'import_type': 'import', 'module': 're', 'line': 5, 'column_offset': 11}
{'import_type': 'import', 'module': 'sys', 'line': 5, 'column_offset': 16}

注意——我刚刚注意到这里有个bug。我去掉了所有的三引号字符串,但我不会补偿行数中丢失的行。这会很棘手。

从Python 3.10开始,AST.alias对象具有行和列属性。这解决了import语句的问题,因为import语句中导入的名称列表表示为AST.alias对象。

不幸的是,这对from... import没有帮助;在ImportFrom对象中,模块是identifier,它是一个没有属性的简单字符串。(从模块导入的名称是AST.alias对象,因此每个对象都有位置信息。但您需要模块名称的位置。(

尽管如此,语句本身具有行和列属性,甚至早于v3.10,这些属性会告诉您语句的开始和结束位置。因此,您可以使用该信息提取仅由from... import语句组成的切片,然后使用令牌化器模块获取from... import语句中的第二个令牌。(第一个标记是from关键字。(这有点笨拙,但它必须比试图用正则表达式攻击Python源代码更容易、更可靠。

最新更新