Вы можете попросить Python сообщить вам о любых возвратах, используя sys.settrace()
;это подключаемая функция, которую Python будет вызывать при возникновении определенных событий, и как обычные отладчики и профилировщики подключаются к Python.
Функция, которую вы регистрируете с помощью sys.settrace()
, будет вызываться для call только события, когда Python входит в новую локальную область (для вызовов функций, тел классов, а также для выражений и выражений генератора).Затем вы можете вернуть None
(не отслеживать эту локальную область) или функцию трассировки, которая будет использоваться для line , исключение или return события в этой области.В Python 3.7 вы можете установить параметры объекта frame для дальнейшего контроля того, на каком уровне детализации вызывается ваша функция трассировки для каждой области;Вы можете отключить события для каждой строки или даже включить события для каждого кода операции.
Вы можете использовать его для записи возврата таких событий;Я использую один метод трассировки для call и return events:
import inspect
import sys
class ReturnLines:
def __init__(self):
self.returns = []
self._old_trace = None
def start(self):
self._old_trace = sys.gettrace()
sys.settrace(self.trace)
def stop(self):
sys.settrace(self._old_trace)
def __enter__(self):
self.start()
return self.returns
def __exit__(self, *exc):
self.stop()
def trace(self, frame, event, arg):
filename = None
if frame is not None:
filename = inspect.getsourcefile(frame)
if event == 'call':
if filename == __file__:
# skip ourselves
return
try:
# Python 3.7+: only trace exceptions and returns for this call
frame.f_trace_lines = False
except AttributeError:
pass
return self.trace
elif event == 'return':
self.returns.append((filename, frame.f_lineno, arg))
Поместите это в отдельный модуль и используйте объект как менеджер контекста:
from return_recorder import ReturnLines
with ReturnLines() as return_lines:
# run the code you want to trace
# ...
Диспетчер контекста предоставляет вам доступ к объекту списка, к которому он добавляет возвраты (как (filename, linenumber, returned_object)
кортежи), так что вы можете получить доступ к информации возврата при выполнении кода внутри диспетчера контекста:
>>> from return_recorder import ReturnLines
>>> def foo(i: int) -> str:
... if i == 1:
... return 'he'
... elif i == 2:
... return 'ha'
... return 'he'
...
>>> with ReturnLines() as return_lines:
... for i in range(3):
... foo(i)
... print(f'<-- i={i}, returned at line {return_lines[-1][1]}')
...
'he'
<-- i=0, returned at line 6
'he'
<-- i=1, returned at line 3
'ha'
<-- i=2, returned at line 5
>>> for filename, lineno, returned in return_lines:
... print(f'{filename}:{lineno}:{returned!r}')
...
None:6:'he'
None:3:'he'
None:5:'ha'
Для интерактивного переводчика имя файла: None
.
Модуль bdb
, лежащий в основе ответа Кевина, основан на sys.set_trace()
, но не отключает трассировку строк в Python 3.7+.,И как универсальная инфраструктура отладчика, она добавляет более высокие издержки на событие трассировки.Это приводит к более медленному выполнению кода, который вы используете.