Есть ли способ подключить интерпретатор CPython, чтобы каждое создание функции (def
, lambda
) приводило к вызову процедуры, которую я определил? sys.settrace
и sys.setprofile
, к сожалению, не охватывают и def
, и lambda
.
Кажется, в Python 3.7 есть f_trace_opcodes
... есть ли вариант для более ранних версий?
@abarnert: Ох, я смотрел только на Python 2, в котором нет событий кода операции. Интересно, посмотрю на Python 3. Спасибо!
Во-первых, вы можете сделать это в терминах строк: вам просто нужно посмотреть на оператор, начинающийся с frame.f_lineno
, и увидеть, является ли он def
(хакерским путем, просто взглянув на что-то вроде inspect.getsource(frame).lstrip().startswith('def ')
, или, что более надежно, например, ast.parse
с исходным кодом). чтобы узнать, является ли это узлом def
. Или, даже если вас вызывают только один раз в строке, вы все равно можете посмотреть коды операций в этой строке, чтобы узнать, есть ли MAKE_FUNCTION
.
@abarnert: Я думаю, что line
рискованно по множеству причин, одна из которых заключается в том, что я не хочу пропускать лямбды (я не думал включать это в вопрос, но должен был сказать, что хочу подключить функцию творчество). opcode
, похоже, то, что я хочу :)
Хорошо, но обратите внимание на понимание (кроме listcomps в 2.x) - это определения функций.
@abarnert: Уф ... Я думаю, что для моих текущих вариантов использования я могу их игнорировать, но это действительно полезно знать, спасибо! Кроме того, я только что заметил, что f_trace_opcodes
предназначен только для Python 3.7 ... Я должен упомянуть 3.6 и ранее в вопросе.
Хорошо, если вы не используете 3.7+ и трассировки строк недостаточно, вы не можете сделать это с settrace
. (Ваш вопрос в основном представляет собой запрос функции для новой функции, и тот факт, что они реализовали этот запрос функции за год до того, как вы попросили об этом, не помогает, если вы не используете последнюю версию ...) Вы, вероятно, все еще можете делать то, что вы хотите, написав сценарий отладчика или написав ловушку импорта, которая вставляет вызовы трассировки в узлы def
/ lambda
в AST или в коды операций MAKE_FUNCTION
в байт-коде, или что-то еще сложное, но ничего простого.
@Mehrdad, это звучит очень интересно: что будет делать ваш код при создании функции?
В версиях до 3.7 нет эквивалента трассировке opcode
. Если бы это было так, эта функция вообще не была бы добавлена в 3.7.
Если вы можете перейти на 3.7, то все, что вам нужно, будет просто:
def tracefunc(frame, event, arg):
if event == 'call':
frame.f_trace_opcodes = True
elif event == 'opcode':
if frame.f_code.co_code[frame.f_lasti] == dis.opmap['MAKE_FUNCTION']:
makefunctiontracefunc(frame)
return tracefunc
sys.settrace(tracefunc)
Но если вы не можете… есть ряд более сложных вещей, которые вы делаете мог, в зависимости от того, по каким причинам вы этого хотите, но ни одна из них не является отдаленно простой:
line
и проверяйте код до следующей строки. Это тривиально для def
, но для lambda
(и понимания 1) это будет большой проблемой, потому что lambda
(или даже пять из них) могут появиться в середине утверждения. Вы можете ast.parse
исходный код или исследовать байт-код, чтобы выяснить, есть ли внутри определенные функции, но все еще нет способа вызвать вашу ловушку прямо во время определения.NodeTransformer
для инъекции вызовов некоторой функции 2 до или после каждого узла def
и lambda
, а затем скомпилируйте преобразованное дерево. Но вы также можете сделать это на уровне байт-кода с помощью bytecode
или byteplay
, до или после каждого MAKE_FUNCTION
.3.pdb
вместо написания собственного отладчика. Я не уверен, что это вообще поможет, потому что pdb
вообще не имеет возможности пройти через часть выражения.MAKE_FUNCTION
в цикле ceval
, который вызывает ваш код. Конечно, ваш код находится в интерпретаторе отладчика, которым может быть Python для gdb
и lldb
, но это все еще не интерпретатор Python тем же, который вы отлаживаете. И хотя можно рекурсивно оценивать код в отлаженном интерпретаторе (или запускать его pdb
), это непросто, и при его работе вы всегда сталкиваетесь с ошибками.1. Comprehensions (except list comprehensions, in 2.x) are implemented by defining and then calling a function. So, any of the methods that rely on the MAKE_FUNCTION
opcode or similar are going to also fire on comprehensions, while those that rely on source or AST parsing will not (unless you do so explicitly, of course).
2. Obviously you also need to inject an import
at the top of every module to make that function available, or inject the function into the builtins module.
3. And MAKE_CLOSURE
, for earlier versions of Python.
settrace
не имеет конкретных событий для большинства типов операторов. Но в нем есть как событияline
, так иopcode
. Итак, вы можете просто проверить, запускает ли строкаdef
или код операции -MAKE_FUNCTION
. (Для 2.7 или 3.5 вам нужно будет проверить, находится ли он вMAKE_FUNCTION
илиMAKE_CLOSURE
, и, IIRC, даже более старые версии Python имели еще больше кодов операций для создания функций, но вы можете просто посмотреть документацию модуляdis
вашей версии, чтобы увидеть. )