2013-08-28 5 views
1

Я пытаюсь собрать исключение в блоке except:, но интерпретатор пытается быть полезным и печатает трассировки стека «силой». Можно ли это избежать?Python3: очистить цепочку исключений?

Немного справочной информации: Я играю с urwid, TUI library для python. Пользовательский интерфейс запускается по телефону urwid.MainLoop.run() и заканчивается повышениемurwid.ExitMainLoop(). Пока это отлично работает, но что происходит, когда возникает другое исключение? Например. когда я поймаю KeyboardInterrupt (у Urwid MainLoop нет), я делаю некоторую очистку и хочу закончить пользовательский интерфейс - подняв соответствующее исключение. Но это приводит к экрану, полному следов стека.

Некоторые небольшие исследования показали, что python3 запоминает прикованные исключения, и можно явно вызвать исключение с «причиной»: raise B() from A(). Я узнал несколько способов изменить или добавить данные относительно вышеперечисленных исключений, но не нашел способа отключить эту функцию. Я хотел бы избежать печати трассировки стека и строк, таких как The above exception was the direct cause of..., и просто поднять исключение, связанное с интерфейсом, в блоке except:, как если бы я был за пределами одного.

Возможно ли это, или я делаю что-то принципиально неправильно?

Edit: Вот пример, напоминающее мою текущую архитектуру, в результате чего в одной и той же проблемой:

#!/usr/bin/env python3 
import time 

class Exit_Main_Loop(Exception): 
    pass 

# UI main loop 
def main_loop(): 
    try: 
     while True: 
      time.sleep(0.1) 
    except Exit_Main_Loop as e: 
     print('Exit_Main_Loop') 
     # do some UI-related clean up 

# my main script 
try: 
    main_loop() 
except KeyboardInterrupt as e: 
    print('KeyboardInterrupt') 
    # do some clean up 
    raise Exit_Main_Loop()  # signal the UI to terminate 

К сожалению, я не могу изменить main_loop к кроме KeyboardInterrupt, а также. Есть ли образец для решения этой проблемы?

+0

Вы вообще не хотите трассировки стека, или хотите только трассировку стека внешнего кода исключения или трассировку стека самого внутреннего исключения? – abarnert

+0

Если вы не хотите трассировки стека _any_, просто оберните весь «основной» код в 'try:'/'except Exception as e:', а затем обработайте исключение, но вы хотите (например, 'print (repr (e)); sys.exit (1) '). – abarnert

+0

Я бы хотел, чтобы ничего не указывало на возникновение исключения (для пользователя) - нет трассировки стека или другого вывода текста (за исключением того, что я печатаю самостоятельно, конечно). Я имею в виду, я выбрал исключение и сделал то, что должен был сделать - больше нет трассировки стека (я бы распечатал его сам, если бы захотел его прочитать). Точно так же urwid.MainLoop тоже выхватывает исключение Exit. Я не вижу причин, почему только потому, что есть два исключения в строке (оба зацепились должным образом), должен привести к результату, напоминающему исключение uncatched. – Julian

ответ

1

я до сих пор не совсем понимаю, ваше объяснение, но из кода:

try: 
    main_loop() 
except KeyboardInterrupt as e: 
    print('KeyboardInterrupt') 
    # do some clean up 
    raise Exit_Main_Loop()  # signal the UI to terminate 

Там нет никакого способа, которым main_loop мог видеть Exit_Main_Loop() исключение. К тому времени, как вы дойдете до ручки KeyboardInterrupt, main_loop, как гарантируется, уже завершен (в данном случае из-за необработанного KeyboardInterrupt), поэтому его обработчик исключений больше не активен.

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

Если вы хотите, чтобы преобразовать один тип исключения в другой так main_loop может справиться с этим, вы должны сделать что-то внутри в try блоке.

Вы говорите:

К сожалению, я не могу изменить main_loop к кроме KeyboardInterrupt, а также.

Если это правда, на ваш вопрос нет реального ответа ... но я не уверен, что есть проблема, в первую очередь, кроме той, которую вы создали. Просто удалите Exit_Main_Loop() с вашего кода, и разве он уже не делает то, что вам нужно? Если вы просто пытаетесь запретить Python печатать трассировку и выходить, это позаботится об этом для вас.


Если действительно является проблемой, например, main_loop код имеет некоторый код очистки, что вам нужно, чтобы получить казнены ни на что, и это не становится выполнено, так как он не обрабатывает KeyboardInterrupt -есть двумя способами вы могли бы обойти это.


Во-первых, как signal документы объясняют:

signal.signal() функция позволяет определить пользовательские обработчики, которые будут выполняться при получении сигнала. Установлено небольшое количество обработчиков по умолчанию: ... SIGINT переведено на исключение KeyboardInterrupt.

Таким образом, все, что вам нужно сделать, это заменить обработчик по умолчанию с другом:

def handle_sigint(signum, frame): 
    raise ExitMainLoop() 
signal.signal(signal.SIGINT, handle_sigint) 

Просто сделайте это, прежде чем начать main_loop, и вы должны быть хорошо. Имейте в виду, что есть некоторые ограничения с потоковыми программами и с Windows, но если ни одно из этих ограничений не применяется, вы являетесь золотым; ctrl-C вызовет исключение ExitMainLoop вместо KeyboardInterrupt, поэтому основной цикл обработает его. (Возможно, вы также захотите добавить блок except ExitMainLoop: в свой код обертки, если есть исключение за пределамиmain_loop. Однако вы можете легко написать contextmanager, который устанавливает и восстанавливает сигнал по вызову до main_loop, так что есть «нет», т любого внешнего кода, который мог бы поднять его.)


в качестве альтернативы, даже если вы не можете редактировать main_loop исходный код, вы всегда можете monkeypatch его во время выполнения. Не зная, как выглядит код, невозможно точно объяснить, как это сделать, но почти всегда есть способ сделать это.

+0

На самом деле 'main_loop()' является внешней (ui) библиотекой, которая должна быть закончена правильно, и это делается путем повышения (также внешнего) 'Exit_Main_Loop'. Я вижу, что у вас нет возможности работать так, как сейчас, но я не знаю, как его лучше моделировать (работать/исправлять). – Julian

+0

Итак, есть ли какие-либо проблемы с основной петлей, которые не делают должного завершения, что вам нужно, потому что она не обрабатывает 'KeyboardInterrupt'? Если это так, есть два способа решения этой проблемы, но они оба будут, по крайней мере, немного уродливыми. Позвольте мне отредактировать ответ, чтобы объяснить. – abarnert

+0

Ну, он выполняет правильную очистку даже в случае «KeyboardInterrupt» (за исключением трассировки печатного стека), но в этом случае мне также нужно немного очистить (до выключения пользовательского интерфейса). Мой первоначальный ответ был немного ... грязным, главным образом потому, что у меня было неправильное понимание самой проблемы, извините. Большое спасибо за ваш ответ, я пойду с обработчиком сигнала. – Julian

Смежные вопросы