исполняемый код в оригинальной локальной рамке - PullRequest
2 голосов
/ 04 августа 2010

Я написал удаленный отладчик Python, и одна из функций, которые мне нужны, - выполнять произвольный код, находясь в точке останова.Мой отладчик использует следующее для выполнения кода, полученного от удаленного отладчика:

exec (compile(code, '<string>', 'single') , frame.f_globals, frame.f_locals)

В большинстве случаев это работает нормально, но я заметил пару проблем.

  1. Операторы присваивания фактически не применяются к оригинальному словарю местных жителей.Вероятно, это связано с тем, что f_locals должен быть доступен только для чтения.

  2. При остановке в методе класса доступ к защищенным атрибутам (имена, начинающиеся с двойного подчеркивания) не работает.Я предполагаю, что это из-за искажения имени, которое Python выполняет с защищенными атрибутами.

Итак, мой вопрос, есть ли способ обойти эти ограничения?Могу ли я обмануть Python, думая, что код выполняется в фактической локальной области этого фрейма?

Я использую CPython 2.7, и я готов принять решение / хак, специфичное для этой версии.

Ответы [ 3 ]

2 голосов
/ 04 августа 2010

Заявления о назначении на самом деле не применяется к оригинальным местным жителям толковый словарь. Это, вероятно, связано с тот факт, что f_locals должен быть только для чтения.

Не совсем, но байт-код для функции не будет смотреть на locals, используя довольно простую, но важную оптимизацию, при которой локальные переменные находятся в простом массиве, избегая поиска во время выполнения. Единственный способ избежать этого (и сделать функцию намного , намного медленнее) - это компилировать другой код, например, код, начинающийся с exec '', чтобы заставить компилятор избегать оптимизации (в Python 2; ни в коем случае в Python 3). Если вам нужно работать с существующим байт-кодом, вам не повезло: у нет способа выполнить то, что вы хотите.

Если остановлено в методе класса, доступ к защищенным атрибутам (имена начиная с двойного подчеркивания) не работа. Я предполагаю, что это связано с искажение имени, которое выполняет Python по защищенным атрибутам.

Да, так что эта проблема делает разрешающим обходной путь: добавьте _Classname к имени, чтобы имитировать то, что делает компилятор. Обратите внимание, что префиксы с двойным подчеркиванием означают, что private : protected будет символом single (и это не доставит вам проблем). Частные имена специально предназначены для того, чтобы избежать случайных классов с именами, связанными в подклассы (и работают достойно для этой единственной цели, хотя и не идеально, и ни для чего другого; -).

0 голосов
/ 09 ноября 2014

для выполнения произвольного кода при остановке на точке останова ... Могу ли я обмануть Питона, заставив его думать, что код выполняется в реальной локальной области этого фрейма?

Это позволяет отладчик Python, pdb. Например, предположим, что вы отлаживаете файл tests/scopeTest.py, и в вашей программе есть следующая строка, где переменная не была объявлена ​​в самой программе:

print (NOT_DEFINED_IN_PROGRAM)

, поэтому выполнение кода python tests/scopeTest.py приведет к:

NameError: name 'NOT_DEFINED_IN_PROGRAM' is not defined

Теперь вы хотели бы определить эту переменную, когда она остановлена ​​на этой строке в отладчике, и продолжить выполнение программы, используя эту переменную, как если бы она была определена в программе все время. Другими словами, вы хотели бы осуществить изменение в этой области, чтобы вы могли продолжить выполнение с этим постоянным изменением. Это действительно возможно:

$ python -m pdb tests/scopeTest.py
> /home/user/tests/scopeTest.py(1)<module>()
-> print (NOT_DEFINED_IN_PROGRAM)
(Pdb) 'NOT_DEFINED_IN_PROGRAM' in locals()
False
(Pdb) NOT_DEFINED_IN_PROGRAM = 5
(Pdb) 'NOT_DEFINED_IN_PROGRAM' in locals()
True
(Pdb) step
5

Pdb делает это через compile и exec в своей функции default, которая эквивалентна:

code = compile(line + '\n', <stdin>, 'single')
exec(code, self.curframe.f_globals, self.curframe_locals)

, где self.curframe - определенный кадр. Теперь self.curframe_locals не является self.curframe.f_locals, потому что, как говорит функция setup:

# The f_locals dictionary is updated from the actual frame
# locals whenever the .f_locals accessor is called, so we
# cache it here to ensure that modifications are not overwritten.
self.curframe_locals = self.curframe.f_locals

Надеюсь, это поможет, и это то, что вы имели в виду!

Обратите внимание, что даже в этом случае, если вы захотите, например, заменить функцию в контексте отлаживаемой программы на версию с исправленными обезьянами, например:

newGlobals['abs'] = myCustomAbsFunction
exec(code, newGlobals, locals)

область действия myCustomAbsFunction не будет пользовательской программой, но будет контекстом, где была определена эта функция, то есть отладчиком! Есть и способ обойти это, но, как об этом специально не спрашивают, на данный момент это оставлено как упражнение для читателя. ^ __ ^

0 голосов
/ 04 августа 2010

Я не уверен, что правильно вас понял, но exec заполняет параметр locals назначениями внутри кода:

>>> loc = {}
>>> exec(compile('a=3', '<string>', 'single'), {}, loc)
>>> loc
{'a': 3}

Возможно, f_locals не разрешает запись.

...