2012-05-09 1 views
3

Для git alias problem я хотел бы иметь возможность выбрать одну функцию Python из файла по имени. например:Сценарий Bash для выбора одной функции Python из файла

... 
    def notyet(): 
     wait for it 

    def ok_start(x): 
     stuff 
     stuff 
     def dontgettrickednow(): 
     keep going 
    #stuff 
     more stuff 

    def ok_stop_now(): 

В алгоритмических терминах, следующий будет достаточно близко:

  1. Начало фильтрации, когда вы найдете строку, которая соответствует /^(\s*)def $1[^a-zA-Z0-9]/
  2. соответствие Keep, пока не найдете строку, которая не^\s*# или ^/\1\s] (то есть, возможно, с отступом или с отступом, превышающим предыдущий)

(Меня не волнует, если декораторы перед тем, как подобрать следующую функцию. Результат для чтения человеком.)

Я пытался сделать это с помощью Awk (чего я едва знаю), но это немного сложнее, чем я думал. Для начала мне понадобится способ хранения длины отступа перед оригиналом def.

ответ

3

В одну сторону: awk. Код хорошо прокомментирован, поэтому я надеюсь, что это легко понять.

Содержание infile:

... 
    def notyet(): 
     wait for it 

    def ok_start(x): 
     stuff 
     stuff 
     def dontgettrickednow(): 
     keep going 
    #stuff 
     more stuff 

    def ok_stop_now(): 

Содержание script.awk:

BEGIN { 
     ## 'f' variable is the function to search, set a regexp with it. 
     f_regex = "^" f "[^a-zA-Z0-9]" 

     ## When set, print line. Otherwise omit line. 
     ## It is set when found the function searched. 
     ## It is unset when found any character different from '#' with less 
     ## spaces before it. 
     in_func = 0 
} 

## Found function. 
$1 == "def" && $2 ~ f_regex { 

     ## Get position of first 'd' in the line. 
     i = index($0, "d") 

     ## Sanity check. Never should success because the condition was 
     ## checked before. 
     if (i == 0) { 
       next 
     } 

     ## Get characters until matched index before, check that all of 
     ## them are spaces, and get its length. 
     indent = substr($0, 0, i - 1) 
     if (indent ~ /^[[:space:]]*$/) { 
       num_spaces = length(indent) 
     } 

     ## Set variable, print line and read next one. 
     in_func = 1 
     print 
     next 
} 

## When we are inside the function, line doesn't begin with '#' and 
## it's not a blank line (only spaces). 
in_func == 1 && $1 ~ /^[^#]/ && $0 ~ /[^[:space:]]/ { 

     ## Get how many characters there are until first non-space. The result 
     ## is the position of first non-blank, so substract one to get the number 
     ## of spaces. 
     spaces = match($0, /[^[:space:]]/) 
     spaces -= 1 

     ## If current indent is less or equal that the indent of function definition, then 
     ## end of function found, so end processing. 
     if (spaces <= num_spaces) { 
       in_func = 0 
     } 
} 

## Self-explanatory. 
in_func == 1 { 
     print 
} 

Выполнить это нравится:

awk -f script.awk -v f="ok_start" infile 

С следующий вывод:

def ok_start(x): 
     stuff 
     stuff 
     def dontgettrickednow(): 
     keep going 
    #stuff 
     more stuff 
+0

Wow, awesome :) Я могу подтвердить, что он работает над моими действиями в реальном мире. Интересным аспектом является наличие двух функций с одним и тем же именем (может случиться, если они находятся в разных классах), он возвращает их обоих из конца в конец. Не уверен, что это было предназначено (или действительно то, что должно быть правильным) - кажется хорошим результатом. –

+0

@SteveBennett: Я просто редактировал, чтобы изменить это поведение. Теперь он должен обрабатывать только первую найденную функцию. – Birei

+0

Я вроде как вариант «выберите все функции с тем же именем» на самом деле - иначе я не уверен, как вы получите доступ ко второму func с тем же именем. –

3

Почему бы просто не позволить python? Я думаю, что модуль inspection может распечатать источник функции, поэтому вы можете просто импортировать модуль, выбрать функцию и проверить ее. Подожди. Отрываясь от решения для вас ...

OK. Оказывается, функция inspect.getsource не работает для материала, определенного в интерактивном режиме:

>>> def test(f): 
...  print 'arg:', f 
... 
>>> test(1) 
arg: 1 
>>> inspect.getsource(test) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "C:\Python27\lib\inspect.py", line 699, in getsource 
    lines, lnum = getsourcelines(object) 
    File "C:\Python27\lib\inspect.py", line 688, in getsourcelines 
    lines, lnum = findsource(object) 
    File "C:\Python27\lib\inspect.py", line 529, in findsource 
    raise IOError('source code not available') 
IOError: source code not available 
>>> 

Но для случая использования, он будет работать: Для модулей, которые сохраняются на диск. Возьмем, например, мой test.py файл:

def test(f): 
    print 'arg:', f 

def other(f): 
    print 'other:', f 

И сравните с этой интерактивной сессии:

>>> import inspect 
>>> import test 
>>> inspect.getsource(test.test) 
"def test(f):\n print 'arg:', f\n" 
>>> inspect.getsource(test.other) 
"def other(f):\n print 'other:', f\n" 
>>> 

Итак ... Вам нужно написать простой скрипт, питона, который принимает имя исходного файла и питона имя функции/объекта в качестве аргументов. Затем он должен импортировать модуль и проверить функцию и распечатать ее в STDOUT.

+0

Мне нравится ваше мышление, но похоже, что Python должен был бы разбирать весь проект, что означало бы проверку всего, а не только одного файла. (В противном случае он не может импортировать, потому что зависимости сломаны) –

+0

oh dear.ну, в таком случае, пакет компилятора python может помочь: http://docs.python.org/library/compiler.html - но это будет намного больше! –

Смежные вопросы