故事:
当在多行上定义字符串列表时,通常很容易忘记列表项之间的逗号,如下例所示:
test = [
"item1"
"item2"
]
列表test
现在只有一个项目"item1item2"
。
重新排列列表中的项目后,通常会出现问题。
具有此问题的Stack Overflow示例问题:
问题:
有没有办法,最好使用静态代码分析,在这种情况下发出警告,以便尽早发现问题?
答案 0 :(得分:19)
这些只是可能的解决方案,因为我不太适合静态分析。
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 ','
标记,表示存在由逗号分隔的第二个元素。
根据这些信息,我们可以在generate_tokens
模块中使用非常方便的方法tokenize
。 tokenize.tokenize()
中的方法tokenize.generate_tokens()
,Py3
有一个参数readline
,一个类似文件的对象的方法,它基本上返回该文件的下一行,如object({{ 3}})。它返回一个带有5个元素的命名元组,其中包含有关令牌类型,令牌字符串以及行号和行中位置的信息。
使用此信息,理论上可以循环一个文件,并且当列表初始化中不存在OP ','
时(通过检查标记NAME
,OP '='
和OP '['
存在于同一行号上)可以在检测到它的行上发出警告。
这种方法的好处在于,概括起来非常简单。要适应字符串文字串联发生的所有情况(即,在'分组'运算符(), {}, []
内),您检查该标记是relevant answer(或type = 51
)或者(, [, {
中任何一个值存在于同一行(这些是粗略的,顶部的建议atm)。
现在,我不确定其他人是如何解决这些问题的 但是 似乎它可能是你可以做的事情看看。所有必要的信息都由tokenize
提供,检测它的逻辑是唯一缺失的。
实施注意事项:这些值(例如,type
)在版本之间确实有所不同,并且可能会发生变化,因此需要注意这些值。但是,人们可以利用这个53 for Python 3来代替令牌。
parser
和ast
:可能更繁琐的另一个可能的解决方案可能涉及by only working with constants和parser
模块。字符串的串联实际上是在创建抽象语法树期间执行的,因此您可以在那里检测它。
我真的不想转储我要提及的parser
和ast
方法的完整输出,但是,只是为了确保我们&#39 ;在同一页面上,我将使用以下列表初始化语句:
l_init = """
test = [
"item1"
"item2",
"item3"
]
"""
为了生成解析树,请使用ast
。完成此操作后,您可以使用p = parser.suite(l_init)
查看它(输出太大而无法添加)。您注意到的是三个不同的str
对象item1
,item2
,item3
将有三个条目。
另一方面,当使用p.tolist()
创建AST并使用node = ast.parse(l_init)
查看时,只有两个条目:一个用于连接的str
s item1item2
,另一个用于item3
。
所以,这是另一种可行的方法,但正如我之前提到的那样,它更加乏味。我不确定行信息是否可用,您是否处理两个不同的模块。如果您想要在编译器链中更高级地使用内部对象,请将其作为背景思考。
结束评论:作为结束语,在这种情况下,tokenize
方法似乎是最合乎逻辑的方法。相反,似乎pylint
实际上与ast.dump(node)
python库一起使用,它简化了对Python代码的抽象语法树的分析。因此,理想情况下应该查看它以及如何使用astroid
。
注意:当然,我可能会完全过度分析它,并且可以更简单地检查空格或换行符'你们建议的解决方案就足够了。 : - )
答案 1 :(得分:2)
我根据@Jim的帖子实现了代码。它可以适用于所有情况:
import tokenize
from io import BytesIO
def my_checker(pycode):
"""
tokenizes python code and yields
start, end, strline of any position where
a scenario like this happens (missing string seperator):
[..., "a string" "derp", ...]
"""
IDLE = 0
WAITING_STRING = 1
CHECKING_SEPARATOR = 2
tokenizer = tokenize.tokenize(BytesIO(pycode.encode('utf-8')).readline)
state = IDLE
for toknum, tokval, start, end, strcode in tokenizer:
if state == IDLE:
if toknum == tokenize.OP and tokval == '[':
state = WAITING_STRING
elif state == WAITING_STRING:
if toknum == tokenize.STRING:
state = CHECKING_SEPARATOR
elif toknum == tokenize.OP and tokval == [']']:
state = IDLE
elif state == CHECKING_SEPARATOR:
if toknum == tokenize.STRING:
yield (start, end, strcode)
elif toknum == tokenize.OP and tokval in ['+', ',']:
state = WAITING_STRING
elif toknum == tokenize.OP and tokval == ']':
state = IDLE
my_code = """
foo = "derp"
def derp(a,x):
return str('dingdong'+str(a*x))
[
"derp"+"FOO22" , "FOO", "donk" "slurp",0, 0
]
class extreme_logical_class():
STATIC_BAD_LIST = [0,
"BLA,",
"FOO"
"derp"
]
def __init__(self):
self._in_method_check = ["A" "B"]
nested_list = [
['DERP','FOO'],
[0,'hello', 'peter' 'pan'],
['this', 'is', ['ultra', 'mega'
'nested']]
]
"""
for error in my_checker(my_code):
print('missing , in list at: line {}@{} to line {}@{}: "{}"'.format(
error[0][0],error[0][1],error[1][0],error[1][1], error[2].strip()
))
结果是:
keksnicoh@localhost ~ % python3 find_bad_lists.py
missing , in list at: line 6@36 to line 6@43: ""derp"+"FOO22" , "FOO", "donk" "blurp",0 0"
missing , in list at: line 13@8 to line 13@14: ""derp""
missing , in list at: line 16@37 to line 16@40: "self._in_method_check = ["A" "B"]"
missing , in list at: line 20@24 to line 20@29: "[0,'hello', 'peter' 'pan'],"
missing , in list at: line 22@8 to line 22@16: "'nested']]"
在现实生活中,我宁愿避免犯这样的错误;有很好的IDE,比如Sublime Text,它允许你用多个光标编辑和格式化列表。如果你习惯了这些概念,这些"分离"错误不会在您的代码中发生。
当然,如果有一个开发人员团队,可以将这样的工具集成到测试环境中。
答案 2 :(得分:1)
此正则表达式将发现问题的发生。只需搜索所有'项目中的文件。
\[("[^"]*",[\s]*)*"[^"]*"[\s]*"
在https://regex101.com/和NotePad ++
中测试过