使用 libcst 在 Python 中查找紧跟 raise 节点的 if 节点
Find if-nodes that are immediately followed by a raise-node in Python with libcst
现在我正在为大学课程做一个项目。我得到了一些随机函数,其中大部分在代码中的某处都有一个 if-raise-statement。
我试图找到那些,但只有那 1 或 2 行。我将函数转换为 AST,然后使用 libcst 访问它。我扩展了访问者 class,搜索 if-nodes,然后匹配 raise-nodes。然而,这也匹配并保存类似 if-if-raise 或 if-else-raise 的语句。
我希望有人能帮助我如何修改匹配器以只匹配if-nodes直接跟在1个raise节点。 (序列通配符匹配器会很棒,但据我所知,它们无法匹配以查找节点序列。)
import libcst as cst
import libcst.matchers as m
class FindIfRaise(cst.CSTVisitor):
if_raise = []
# INIT
def __init__(self):
self.if_raise = []
def visit_If(self, node: cst.If):
try:
if m.findall(node, m.Raise()):
self.if_raise.append(node)
在此先感谢您的帮助。
您可以递归遍历 cst
,而不是节点访问者模式,通过每个 cst
对象的 body
属性进行导航。这样您就可以跟踪您的深度,检查同级 if
语句,并且仅在满足所需条件时生成 raise
语句:
import libcst as cst
def walk(ct, p = []):
bd = ct
while (not isinstance(bd:=getattr(bd, 'body', []), list)): pass
for i, t in enumerate(bd):
if isinstance(t, cst._nodes.statement.Raise):
f = False
for i in p[::-1]:
if not isinstance(i, (cst._nodes.statement.IndentedBlock, cst._nodes.statement.SimpleStatementLine)):
f = isinstance(i, cst._nodes.statement.If)
break
if f: yield t
elif isinstance(t, cst._nodes.statement.If):
if t.orelse is None and (i == len(bd) - 1 or not isinstance(bd[i + 1], cst._nodes.statement.If)):
yield from walk(t, p + [t])
else:
yield from walk(t, p + [t])
s = """
if something:
raise Exception
if something_else:
pass
"""
print([*walk(cst.parse_module(s))]) #[], since `if something` is followed by another if-statement
s1 = """
if something:
raise Exception
elif something_else:
pass
"""
print([*walk(cst.parse_module(s1))]) #[], since `if something` is followed by an elif-statement
s2 = """
if something:
raise Exception
for i in range(10): pass
"""
print([*walk(cst.parse_module(s2))]) #[Raise(
# exc=Name(
# value='Exception',
# lpar=[],
# rpar=[],
# ),
# cause=None,
# whitespace_after_raise=SimpleWhitespace(
# value=' ',
# ),
# semicolon=MaybeSentinel.DEFAULT,
#)]
现在我正在为大学课程做一个项目。我得到了一些随机函数,其中大部分在代码中的某处都有一个 if-raise-statement。
我试图找到那些,但只有那 1 或 2 行。我将函数转换为 AST,然后使用 libcst 访问它。我扩展了访问者 class,搜索 if-nodes,然后匹配 raise-nodes。然而,这也匹配并保存类似 if-if-raise 或 if-else-raise 的语句。
我希望有人能帮助我如何修改匹配器以只匹配if-nodes直接跟在1个raise节点。 (序列通配符匹配器会很棒,但据我所知,它们无法匹配以查找节点序列。)
import libcst as cst
import libcst.matchers as m
class FindIfRaise(cst.CSTVisitor):
if_raise = []
# INIT
def __init__(self):
self.if_raise = []
def visit_If(self, node: cst.If):
try:
if m.findall(node, m.Raise()):
self.if_raise.append(node)
在此先感谢您的帮助。
您可以递归遍历 cst
,而不是节点访问者模式,通过每个 cst
对象的 body
属性进行导航。这样您就可以跟踪您的深度,检查同级 if
语句,并且仅在满足所需条件时生成 raise
语句:
import libcst as cst
def walk(ct, p = []):
bd = ct
while (not isinstance(bd:=getattr(bd, 'body', []), list)): pass
for i, t in enumerate(bd):
if isinstance(t, cst._nodes.statement.Raise):
f = False
for i in p[::-1]:
if not isinstance(i, (cst._nodes.statement.IndentedBlock, cst._nodes.statement.SimpleStatementLine)):
f = isinstance(i, cst._nodes.statement.If)
break
if f: yield t
elif isinstance(t, cst._nodes.statement.If):
if t.orelse is None and (i == len(bd) - 1 or not isinstance(bd[i + 1], cst._nodes.statement.If)):
yield from walk(t, p + [t])
else:
yield from walk(t, p + [t])
s = """
if something:
raise Exception
if something_else:
pass
"""
print([*walk(cst.parse_module(s))]) #[], since `if something` is followed by another if-statement
s1 = """
if something:
raise Exception
elif something_else:
pass
"""
print([*walk(cst.parse_module(s1))]) #[], since `if something` is followed by an elif-statement
s2 = """
if something:
raise Exception
for i in range(10): pass
"""
print([*walk(cst.parse_module(s2))]) #[Raise(
# exc=Name(
# value='Exception',
# lpar=[],
# rpar=[],
# ),
# cause=None,
# whitespace_after_raise=SimpleWhitespace(
# value=' ',
# ),
# semicolon=MaybeSentinel.DEFAULT,
#)]