这些只是可能的解决方案,因为我不太擅长静态分析。
与tokenize:
我最近在with tokenizing python code 周围摆弄,我相信当添加足够的逻辑时,它具有执行此类检查所需的所有信息。对于您给定的列表,使用python -m tokenize list1.py 生成的令牌如下:
python -m tokenize list1.py
1,0-1,4: NAME 'test'
1,5-1,6: OP '='
1,7-1,8: OP '['
1,8-1,9: NL '\n'
2,1-2,8: STRING '"item1"'
2,8-2,9: NL '\n'
3,1-3,8: STRING '"item2"'
3,8-3,9: NL '\n'
4,0-4,1: OP ']'
4,1-4,2: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
这当然是“有问题的”的情况,内容将被连接起来。在存在, 的情况下,输出会略有变化以反映这一点(我仅为列表主体添加了标记):
1,7-1,8: OP '['
1,8-1,9: NL '\n'
2,1-2,8: STRING '"item1"'
2,8-2,9: OP ','
2,9-2,10: NL '\n'
3,1-3,8: STRING '"item2"'
3,8-3,9: NL '\n'
4,0-4,1: OP ']'
现在我们有了额外的OP ',' 标记,表示存在以逗号分隔的第二个元素。
鉴于这些信息,我们可以在tokenize 模块中使用非常方便的方法generate_tokens。方法tokenize.generate_tokens() ,tokenize.tokenize() 在Py3 中只有一个参数readline,这是一种在类文件对象上的方法,它本质上为类文件对象(relevant answer)返回下一行。它返回一个共有 5 个元素的命名元组,其中包含有关令牌类型、令牌字符串以及行号和行中位置的信息。
使用此信息,理论上可以循环遍历文件,并且当列表初始化中不存在OP ',' 时(通过检查标记NAME、OP '=' 和OP '[' 是否存在于相同的行号)可以在检测到它的行上发出警告。
这种方法的好处在于它可以很直接地概括。为了适应字符串文字连接发生的所有情况(即,在“分组”运算符 (), {}, [] 内),您检查令牌是否为 type = 51(或 53 for Python 3)或是否存在 (, [, { 中的任何值在同一行上(这些是粗略的,最重要的建议 atm)。
现在,我不太确定其他人是如何解决这些问题的 但 看起来你可以研究一下。 tokenize 提供了所有必要的信息,唯一缺少的就是检测它的逻辑。
实施说明:这些值(例如,type)在版本之间确实不同,并且可能会发生变化,因此应该注意这一点。不过,人们可能会利用这个by only working with constants 来获取令牌。
使用parser 和ast:
另一个可能更乏味的解决方案可能涉及parser 和ast 模块。字符串的连接实际上是在抽象语法树的创建过程中执行的,因此您也可以在那里检测它。
我真的不想转储我将要提到的 parser 和 ast 方法的完整输出,但是,为了确保我们在同一页面上,我将使用以下列表初始化语句:
l_init = """
test = [
"item1"
"item2",
"item3"
]
"""
为了生成解析树,请使用p = parser.suite(l_init)。完成后,您可以使用p.tolist() 查看它(输出太大,无法添加)。您注意到三个不同的str 对象item1、item2、item3 将有三个条目。
另一方面,当使用 node = ast.parse(l_init) 创建 AST 并使用 ast.dump(node) 查看时,只有两个条目:一个用于连接的 strs item1item2,另一个用于另一个条目item3。
所以,这是另一种可能的方法,但正如我之前提到的,它更乏味。我不确定线路信息是否可用并且您处理两个不同的模块。如果您可能想在编译器链中使用更高的内部对象,请将其作为一个回想。
结束评论:作为结束说明,tokenize 方法在这种情况下似乎是最合乎逻辑的。相反,似乎pylint 实际上与astroid 一起工作,这是一个简化python 代码抽象语法树分析的python 库。所以,理想情况下应该看看它以及它是如何使用的inside pylint。
注意:当然,我可能完全过度分析了它,而你们建议的更简单的“检查空格或换行符”解决方案就足够了。 :-)