Многострочные выражения Python и трассировка стека

У нас есть простая функция AssertTrue, используемая в нашем проекте Python, и я хотел изменить вывод, который она предоставляет, чтобы распечатать оператор кода, из которого она была вызвана. Код выглядит примерно так:

1 import traceback
2
3 def AssertTrue(expr, reason=None):
4     print traceback.format_stack()[-2]
5
6 AssertTrue(1 == 2,
7         reason='One is not equal to two')

Выход:

File "/tmp/fisken.py", line 7, in <module>
  reason='One is not equal to two')

Мне интересно, почему traceback.format_stack дает мне код только в строке 7. Оператор начинается в строке 6, и выражение, которое я хотел бы видеть в выводе, также находится в той же строке. Разве трассировка не обрабатывает многострочные вызовы функций?

(Неважно, что есть лучшие способы сделать AssertTrue(...). Мне просто интересно, почему traceback.format_stack (и .extract_stack) ведет себя не так, как я ожидал)


person Vegar Westerlund    schedule 16.07.2012    source источник


Ответы (1)


Разве трассировка не обрабатывает многострочные вызовы функций?

Многие функции состоят из десятков или даже (ужас) сотен строк. Если бы traceback печатал всю функцию, то трассировка стека стала бы непостижимо длинной. Поэтому я предполагаю, что то, что вы видите, — это попытка сохранить чистоту и минимум.

Я собрал несколько ответов на похожие вопросы:

С учетом того, что он может получить только источник для всей функции (если источник доступен на пути), я могу предложить вам следующее:

import traceback
import inspect
import gc

def giveupthefunc(frame):
    code  = frame.f_code
    globs = frame.f_globals
    functype = type(lambda: 0)
    funcs = []
    for func in gc.get_referrers(code):
        if type(func) is functype:
            if getattr(func, "func_code", None) is code:
                if getattr(func, "func_globals", None) is globs:
                    funcs.append(func)
                    if len(funcs) > 1:
                        return None
    return funcs[0] if funcs else None


def AssertTrue(expr, reason=None):
    print traceback.format_stack()[-2]
    frame = inspect.currentframe().f_back
    func = giveupthefunc(frame)
    if func:
        source = inspect.getsourcelines(func)
        i = source[1]
        for line in source[0]:
            print i, ":", line,
            i += 1



def my_fun():
    AssertTrue(1 == 2,
             reason='One is not equal to two')

my_fun()

Что производит:

/Library/Frameworks/Python.framework/Versions/2.7/bin/python /Users/xxxx/Documents/PycharmProjects/scratchpad/test.py
  File "/Users/xxxx/Documents/PycharmProjects/scratchpad/test.py", line 35, in my_fun
    reason='One is not equal to two')

33 : def my_fun():
34 :     AssertTrue(1 == 2,
35 :              reason='One is not equal to two')
person Andrew Alcock    schedule 17.07.2012
comment
Я думаю, вы правы насчет предположения о длине. Функция трассировки будет печатать только одну строку, чтобы ограничить вывод. Код, который вы предоставили, выглядит как красивый хак. Спасибо... :-) - person Vegar Westerlund; 17.07.2012
comment
Я, конечно, хотел бы получить только оператор assert, а не всю функцию в виде строки. - person Vegar Westerlund; 17.07.2012
comment
На данный момент это выходит за рамки моих хакерских навыков. Если я что-то придумаю, я вернусь к вам :) - person Andrew Alcock; 18.07.2012