Многострочные выражения 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) не ведет себя так, как я ожидал)
1 ответ
Разве отслеживание не обрабатывает многострочные вызовы функций?
Многие функции имеют десятки или даже (ужасы) сотни строк. Если traceback действительно напечатал всю функцию, то трассировка стека стала бы непостижимо длинной. Поэтому я думаю, что вы видите, это попытка сохранить вещи чистыми и минимальными.
Я собрал несколько ответов на похожие вопросы:
- Python код для получения текущей функции в переменную?
- Как я могу получить исходный код функции Python?
С учетом того, что он может проверить только источник всей функции (если источник доступен по пути), я могу предложить вам следующее:
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')