Как использовать регулярные выражения, чтобы найти функции, которые сами вызывают? - PullRequest
1 голос
/ 04 января 2012

У меня есть code.py:

def funA():
    print('A')
    funA()

def funB():
    print('B')

def funC():
    print('C')
    funB()
    funC()

Я хочу найти все функции, которые сами вызывают:

funA
funC

Как написать regex?

Constraint:

  • Все вызовы функций в норме: funname(arg1, arg2, ...)
  • Нет запутанных путей (например, lambda, exec)
  • Нет косвенной рекурсии

Ответы [ 5 ]

2 голосов
/ 04 января 2012

Это сложно, потому что функция может вызывать себя запутанными способами.Например, это считается?

def funA():
  print 'A'
  foo = funA
  foo()

funA()

Как насчет этого?

def funA():
  funB()

def funB():
  funA()

funA()

Или даже это?

def funA():
  exec('Anuf'[::-1] + '()')

funA()

Я не думаю, что вы можете сделать этос регулярным выражением


Даже с учетом ваших новых правок все равно будет очень сложно, если не невозможно.Рассмотрим эту функцию для примера.

def funA():
  if 1 + 1 == 2:
    return
  funA()

Я предлагаю вам последовать совету Игнасио Васкеса-Абрамса и взглянуть на ast .

1 голос
/ 04 января 2012

Да, я верю, что для регулярного выражения было бы невозможно сопоставить случаи, указанные в wim, когда вызов self скрыт.Тем не менее, здесь есть регулярное выражение, которое будет выполнять приличную работу для прямых вызовов по собственной личности (в форме funcname(...)).Это регулярное выражение правильно сопоставляет все контрольные примеры, изложенные в исходном вопросе:

reobj = re.compile(r"""
    # Match (unreliably) Python function with self reference.
    ^                        # Anchor to start of line.
    ([ \t]*)                 # $1: Indentation of DEF statement.
    def[ \t]+                # Function definition.
    ([^\s(]+)                # $2: Name of function to find.
    .*\r?\n                  # Remainder of function def line.
    (?:                      # Zero or more lines w/o funcname.
      (?:                    # Function block lines alternatives.
        \1[ \t]+             # Func block lines have extra indentation.
        (?:(?!\b\2\s*\().)*  # Optional non-funcname stuff on line
      | [ \t]*\#.*           # Allow comment lines to defy indent rules.
      )?                     # Allow blank lines in function block.
      \r?\n                  # End of line not containing funcname.
    )*                       # Zero or more lines w/o funcname
    \1[ \t]+                 # Now match the line having funcname.
    (?:(?!\b\2\s*\().)*      # Optional non-funcname stuff on line
    \b\2\s*\(                # Match the function self reference.
    """, re.MULTILINE | re.VERBOSE)

Соответствует начиная со строки определения функции и фиксирует отступы перед пробелом 'def' в группе $1 и функцииимя в группе $2.Затем он сопоставляет строки в функциональном блоке, которые не содержат имя функции, каждое из которых имеет больше начальных пробелов, чем определение функции.Он пропускает пустые строки и строки, содержащие только комментарии.Он объявляет совпадение, как только находит в функциональном блоке строку с именем функции, за которым следуют левые скобки, указывающие на вызов самого себя.В противном случае он объявляет несоответствие, а затем продолжает поиск следующего возможного соответствия.

Обратите внимание, что это решение ненадежно и приведет к ложным срабатываниям, если имя функции встречается внутри строки или внутри комментария, следующего задругой код в строке.Он также не обрабатывает функции с многострочными необработанными строками.Тем не менее, он правильно поймает немало!

1 голос
/ 04 января 2012

Проще говоря, вы не сможете сделать это с помощью регулярных выражений. Вам нужно будет по крайней мере разобрать определения функций, сохранить какое-то состояние относительно того, какую функцию вы в данный момент анализируете, и искать вызов текущего имени функции в области действия текущей функции.

0 голосов
/ 04 января 2012

может быть, вы могли бы использовать gawk, ниже приведен мой пример кода, вам может потребоваться изменить его:

#! /usr/bin/gawk -f
{
    currentLine = $0
    if (currentLine ~ /def/){
        inFunction = "true"
        nameIndex = index($2,"(")
        functionName = substr($2,1,nameIndex - 1)
        #print functionName
        next
    }
    if (inFunction == "true" && currentLine ~ functionName){
       inFunction = false 
       print "recursive function is: " functionName
    }
}

просто запустите программу, вы получите то, что вы хотите.

0 голосов
/ 04 января 2012

Я опишу шаблон, который, я думаю, вам нужен: строка, начинающаяся с def, затем пробел, затем имя (которое вы записываете в скобках), за которым следует (возможно, пустой) набор строк, начинающихся с с пробелом, за которым следует строка, которая начинается с пробела и содержит имя вашей функции, за которым следует открытое слово (так что вы фиксируете фактический вызов, а не просто ссылку).

...