Python: проверьте, куда пойдет повышение исключения - PullRequest
2 голосов
/ 28 ноября 2010

Возьмите этот код:

def A():
   try:
      B()
   except Exception:
      pass

def B():
   C()

def C():
   print exception_handling_pointer()

A()

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

<function A ...>

Как я могу реализовать функцию exception_handling_pointer?

Ответы [ 2 ]

3 голосов
/ 28 ноября 2010

Вы не можете решить, где будет обрабатываться Исключение, фактически не вызывая Исключение.Это легко увидеть здесь:

try: 
    raise input('Raise which?')
except input('Catch which?') as e: 
    pass`

Любая функция, которая делает то, что вы хотите, должна была бы предсказать ввод пользователя здесь.Все усилия тщетны, и Python не поддерживает их.

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

2 голосов
/ 29 ноября 2010

Это довольно глупая вещь, и большинство людей сказали бы, что это невозможно, (THC4k дает убедительные доказательства этого для общего корпуса), но это звучит забавно и должно быть вполне выполнимо во многих реальных целях.случаи.

шаг 1 .Вы должны сделать шаг назад через кадры.Получите первый с sys._getframe или inspect.currentframe (не говорите никому, второй кажется псевдонимом первого).Затем вы можете перебирать их с помощью f.f_back

шаг 2 .У каждого будет инструкция f.f_lasti.Это последняя инструкция, которая была выполнена в кадре.Вы должны будете сохранить это.Теперь вернитесь назад через байт-код - f.f_code.co_code - и найдите код операции SETUP_EXCEPT с аргументом, который переходит на после f.f_lasti`.Точкой перехода является обработка исключений.

шаг 3 .Это где это становится нечетким.Ключ в том, что фактической операцией сравнения будет COMPARE_OP с 10 в качестве аргумента.Во всех случаях, которые я видел, за этим следует POP_JUMP_IF_FALSE.Это перейдет к следующему предложению except или предложению finally.Ему будет предшествовать код, который загружает загружаемые исключения в стек.Если есть только один, то это будет прямой LOAD_GLOBAL или LOAD_GLOBAL или LOAD_FAST (в зависимости от того, является ли модуль с исключениями глобальным или локальным), за которым следует LOAD_ATTR.Если сопоставляются несколько исключений, то будет последовательность операций загрузки, за которыми следует BUILD_TUPLE (идиоматический) или BUILD_LIST (некоторая другая странная или неидиоматическая ситуация).

Дело в том, чтоВы можете выполнить инструкции LOAD_X и сравнить имя с соответствующим исключением.Обратите внимание, что вы сравниваете только имя .Если они переназначили имя, вы SOL.

шаг 4 .Давайте предположим, что вы нашли совпадение.Теперь вам нужен объект функции.Лучший способ сделать это следующим образом (я оставляю за собой право на обновление): f.f_code будет иметь атрибут co_filename.Вы можете перебрать sys.modules, и у каждого будет атрибут __name__.Вы можете сравнить два, имея в виду, что вы должны использовать __name__.endswith(co_filename).Когда вы получите совпадение, вы можете зациклить функции модулей и сравнить их атрибут f.func_code.co_firstlineno с атрибутом frames f.f_lineno.Когда вы получаете совпадение, у вас есть ваша функция.Вы должны также пройтись по методам каждого класса в модуле.Существует вероятность того, что обработка происходит в какой-то вложенной функции, и в этом случае я не могу сейчас придумать разумную вещь, которую нужно сделать.(Это был бы совершенно другой взлом байт-кода и сам по себе был бы ненадежным)

step 5 .Прибыль.

Это должно дать вам общее представление о том, как это сделать.Существуют всевозможные угловые случаи, когда вы не сможете это сделать, но в любом обычном случае вы должны быть в состоянии осуществить это.Если вы напишите код, который зависит от того, сможете ли вы это сделать, то все равно сломается .Это что-то вроде «Делай, потому что я могу».

...