Найдите узлы if, за которыми сразу следует узел повышения в Python с помощью libcst
сейчас я работаю над проектом для университетского курса. У меня есть несколько случайных функций, и у большинства из них где-то в коде есть оператор if-raise.
Я пытаюсь найти такие, но только эти 1 или 2 строки. Я преобразовываю функции в AST, а затем посещаю его с помощью libcst. Я расширяю класс посетителя, ищу узлы if, а затем сопоставляю узлы повышения. Однако это также соответствует и сохраняет операторы типа if-if-raise или if-else-raise.
Я надеюсь, что кто-нибудь может помочь мне в том, как изменить сопоставитель так, чтобы он соответствовал только узлам if, за которыми непосредственно следует 1 узел повышения. (Сопоставители подстановочных знаков последовательности были бы потрясающими, но, насколько я понимаю, их нельзя сопоставлять для поиска последовательностей узлов.)
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)
Заранее благодарю за любую помощь.
2 ответа
Вы хотели бы что-то вроде этого:
import libcst.matchers as m
maybe_more = m.AtLeastN(m.DoNotCare(), n=0) # anything or nothing
raise_block = [m.Raise(), maybe_more] # raise followed by anything or nothing
single_line_body = m.SimpleStatementSuite(body=raise_block)
multi_line_body = m.IndentedBlock(
body=[m.SimpleStatementLine(body=raise_block), maybe_more]
)
if_raise = m.If(body=single_line_body | multi_line_body)
Это должно соответствовать всем следующимif
заявления:
if foo: raise Bar(); blah()
if foo: raise Bar()
if foo:
raise Bar()
if foo:
raise Bar()
blah()
if foo:
raise Bar(); blah()
Вместо шаблона посетителя узла вы можете рекурсивно перемещаться по
body
атрибут каждого
cst
объект. Таким образом, вы можете отслеживать свою глубину, проверять наличие братьев и сестер.
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,
#)]