These are patches that fix and enhance behaviour of exception handling in some specific cases.
PATCH 1/3 adds the code that moves exception handling in text mode to the main thread even if an exception appears in a non-main thread. This is needed for various reasons and it is implemented using the asynchronous message handling in text mode. The basic idea is described in the commit message. And it needs a patch for python-meh that is being proposed so that we can tell python-meh to use our raw_input instead of the standard one that doesn't work with threads.
PATCH 2/3 only adds some logging that becomes really handy in case exception handling doesn't work as expected.
PATCH 3/3 fixes the issue with the rescue mode. It adds some needed pieces to the RescueInterface, moves the creation of its instance to a better place and adds a class for exception handling interface correclty shutting down snack before running the exception handler.
Vratislav Podzimek (3): Run exception handling in the main thread also in TUI Add logging to exception handling Make exception handling in the rescue mode work
anaconda | 8 ++-- pyanaconda/exception.py | 85 ++++++++++++++++++++++++++---------- pyanaconda/rescue.py | 25 +++++++++-- pyanaconda/ui/tui/__init__.py | 27 +++++++++++- pyanaconda/ui/tui/simpleline/base.py | 36 +++++++++++---- 5 files changed, 141 insertions(+), 40 deletions(-)
We need to get exception handling to the main thread in both graphical and textual user interface. This patch adds the code necessary to get exception handling to the main thread in text mode by using the async handling based on the communication queue. We just need to distinguish between exception in the main thread or in a non-main thread.
If the exception appears in a non-main thread a message with the exception data is sent to the queue instead of running the exception handling. When such message is handled (which happens in the main thread), the exception handler is run again and this time it runs the exception handling.
We need to tell python-meh to use our raw_input because the standard one can be called only once (the other calls are queued) and thus the one called by the TUI will block the python-meh's one. And then the same applies to Pdb session run from the python-meh and so on.
Also use namedtuples as python-meh does now. --- pyanaconda/exception.py | 76 ++++++++++++++++++++++++------------ pyanaconda/ui/tui/__init__.py | 27 ++++++++++++- pyanaconda/ui/tui/simpleline/base.py | 36 ++++++++++++----- 3 files changed, 105 insertions(+), 34 deletions(-)
diff --git a/pyanaconda/exception.py b/pyanaconda/exception.py index 0119ebc..81f2a87 100644 --- a/pyanaconda/exception.py +++ b/pyanaconda/exception.py @@ -33,7 +33,10 @@ import time from flags import flags import kickstart import blivet.errors +from pyanaconda.ui import communication from pyanaconda.constants import ROOT_PATH +from pyanaconda.threads import threadMgr +# pylint: disable-msg=E0611 from gi.repository import GLib
import logging @@ -55,23 +58,29 @@ class AnacondaExceptionHandler(ExceptionHandler): ExceptionHandler.__init__(self, confObj, intfClass, exnClass) self._intf_tty_num = tty_num
- def handleException(self, (ty, value, tb), obj): + def run_handleException(self, dump_info): + """ + Helper method with one argument only so that it can be registered + with GLib.idle_add() to run on idle or called from a handler. + + @type dump_info: an instance of the meh.DumpInfo class
- def run_handleException_on_idle(args_tuple): - """ - Helper function with one argument only so that it can be registered - with GLib.idle_add() to run on idle. + """
- @param args_tuple: ((ty, value, tb), obj) + super(AnacondaExceptionHandler, self).handleException(dump_info) + return False + + def handleException(self, dump_info): + """ + Our own handleException method doing some additional stuff before + calling the original python-meh's one.
- """ + @type dump_info: an instance of the meh.DumpInfo class + @see: python-meh's ExceptionHandler.handleException
- trace, obj = args_tuple - ty, value, tb = trace + """
- super(AnacondaExceptionHandler, self).handleException((ty, value, tb), - obj) - return False + ty = dump_info.exc_info.type
if issubclass(ty, blivet.errors.StorageError) and value.hardware_fault: hw_error_msg = _("The installation was stopped due to what " @@ -82,25 +91,45 @@ class AnacondaExceptionHandler(ExceptionHandler): sys.exit(0) else: try: + # pylint: disable-msg=E0611 from gi.repository import Gtk
+ # XXX: Gtk stopped raising RuntimeError if it fails to + # initialize. Horay! But will it stay like this? Let's be + # cautious and raise the exception on our own to work in both + # cases + (initialized, args) = Gtk.init_check(None) + if not initialized: + raise RuntimeError() + if Gtk.main_level() > 0: # main loop is running, don't crash it by running another one # potentially from a different thread - GLib.idle_add(run_handleException_on_idle, - ((ty, value, tb), obj)) + GLib.idle_add(self.run_handleException, dump_info) else: + log.info("running handler") super(AnacondaExceptionHandler, self).handleException( - (ty, value, tb), obj) + dump_info)
except RuntimeError: # X not running (Gtk cannot be initialized) - print "An unknown error has occured, look at the "\ - "/tmp/anaconda-tb* file(s) for more details" - super(AnacondaExceptionHandler, self).handleException( - (ty, value, tb), obj) + if threadMgr.in_main_thread(): + print "An unknown error has occured, look at the "\ + "/tmp/anaconda-tb* file(s) for more details" + # in the main thread, run exception handler + super(AnacondaExceptionHandler, self).handleException( + dump_info) + else: + # not in the main thread, just send message with exception + # data and let message handler run the exception handler in + # the main thread + exc_info = dump_info.exc_info + communication.send_exception((exc_info.type, exc_info.value, + exc_info.stack)) + + def postWriteHook(self, dump_info): + anaconda = dump_info.object
- def postWriteHook(self, (ty, value, tb), anaconda): # See if /mnt/sysimage is present and put exception there as well if os.access("/mnt/sysimage/root", os.X_OK): try: @@ -116,7 +145,7 @@ class AnacondaExceptionHandler(ExceptionHandler): except: pass
- def runDebug(self, (ty, value, tb)): + def runDebug(self, exc_info): if self._intf_tty_num != 1: iutil.vtActivate(1)
@@ -146,7 +175,7 @@ class AnacondaExceptionHandler(ExceptionHandler): print("Use 'continue' command to quit the debugger and get back to "\ "the main window") import pdb - pdb.post_mortem (tb) + pdb.post_mortem(exc_info.stack)
if self._intf_tty_num != 1: iutil.vtActivate(self._intf_tty_num) @@ -228,8 +257,7 @@ f%s(msg, non_ascii) msg = "NOTABUG: testing exception handling"
# raise exception from a separate thread - # XXX: may create a circular dependency if imported globally - from pyanaconda.threads import AnacondaThread, threadMgr + from pyanaconda.threads import AnacondaThread threadMgr.add(AnacondaThread(name="AnaExceptionHandlingTest", target=raise_exception, args=(msg, non_ascii))) diff --git a/pyanaconda/ui/tui/__init__.py b/pyanaconda/ui/tui/__init__.py index 3a76045..e23c588 100644 --- a/pyanaconda/ui/tui/__init__.py +++ b/pyanaconda/ui/tui/__init__.py @@ -21,6 +21,7 @@
from pyanaconda import ui from pyanaconda.ui import common +from pyanaconda.ui.communication import hubQ, HUB_CODE_EXCEPTION from pyanaconda.flags import flags import simpleline as tui from hubs.summary import SummaryHub @@ -29,12 +30,30 @@ from spokes import StandaloneSpoke from tuiobject import YesNoDialog, ErrorDialog
import os +import sys import site import meh.ui.text
import gettext _ = lambda x: gettext.ldgettext("anaconda", x)
+def exception_msg_handler(event, data): + """ + Handler for the HUB_CODE_EXCEPTION message in the hubQ. + + @param event: event data + @type event: (event_type, message_data) + @param data: additional data + @type data: any + + """ + + # get data from the event data structure + (event_type, msg_data) = event + + # msg_data is a list + sys.excepthook(*msg_data[0]) + class TextUserInterface(ui.UserInterface): """This is the main class for Text user interface."""
@@ -118,7 +137,13 @@ class TextUserInterface(ui.UserInterface): This method must be provided by all subclasses. """ self._app = tui.App(self.productTitle, yes_or_no_question = YesNoDialog, - quit_message = self.quitMessage) + quit_message = self.quitMessage, queue = hubQ) + + # tell python-meh it should use our raw_input + self._meh_interface.set_io_handler(meh.ui.text.IOHandler(in_func=self._app.raw_input)) + + # register handler for the exception messages + self._app.register_event_handler(HUB_CODE_EXCEPTION, exception_msg_handler) _hubs = self._list_hubs()
# First, grab a list of all the standalone spokes. diff --git a/pyanaconda/ui/tui/simpleline/base.py b/pyanaconda/ui/tui/simpleline/base.py index 0aabe67..cd0869b 100644 --- a/pyanaconda/ui/tui/simpleline/base.py +++ b/pyanaconda/ui/tui/simpleline/base.py @@ -21,9 +21,11 @@
__all__ = ["App", "UIScreen", "Widget"]
+import sys import readline import Queue import getpass +import threading from pyanaconda.threads import threadMgr, AnacondaThread from pyanaconda.ui.communication import HUB_CODE_EXCEPTION, HUB_CODE_INPUT
@@ -31,6 +33,8 @@ import gettext _ = lambda x: gettext.ldgettext("anaconda", x) N_ = lambda x: x
+RAW_INPUT_LOCK = threading.Lock() + def send_exception(queue, ex): queue.put((HUB_CODE_EXCEPTION, [ex]))
@@ -91,6 +95,9 @@ class App(object): # ensure unique thread names self._in_thread_counter = 0
+ # do not run multiple raw_inputs (doesn't work) + self._raw_input_running = False + # event handlers # key: event id # value: list of tuples (callback, data) @@ -145,7 +152,18 @@ class App(object): if hidden: data = getpass.getpass(prompt) else: - data = raw_input(prompt) + sys.stdout.write(prompt) + sys.stdout.flush() + # XXX: only one raw_input can run at a time, don't schedule another + # one as it would cause weird behaviour and block other packages' + # raw_inputs + if not RAW_INPUT_LOCK.acquire(False): + # raw_input is already running + return + else: + # lock acquired, we can run raw_input + data = raw_input() + RAW_INPUT_LOCK.release()
queue.put((HUB_CODE_INPUT, [data]))
@@ -269,8 +287,8 @@ class App(object): self._redraw = False except ExitMainLoop: raise - except Exception as ex: - send_exception(self.queue, ex) + except Exception: + send_exception(self.queue, sys.exc_info()) return False
else: @@ -328,8 +346,8 @@ class App(object): prompt = last_screen.prompt(self._screens[-1][1]) except ExitMainLoop: raise - except Exception as ex: - send_exception(self.queue, ex) + except Exception: + send_exception(self.queue, sys.exc_info()) continue
# None means prompt handled the input by itself @@ -385,8 +403,8 @@ class App(object): handler(event, data) except ExitMainLoop: raise - except Exception as ex: - send_exception(self.queue, ex) + except Exception: + send_exception(self.queue, sys.exc_info())
def raw_input(self, prompt, hidden=False): """This method reads one input from user. Its basic form has only one @@ -425,8 +443,8 @@ class App(object): return True except ExitMainLoop: raise - except Exception as ex: - send_exception(self.queue, ex) + except Exception: + send_exception(self.queue, sys.exc_info()) return False
# global close command
On 03/26/2013 02:51 PM, Vratislav Podzimek wrote:
@@ -91,6 +95,9 @@ class App(object): # ensure unique thread names self._in_thread_counter = 0
# do not run multiple raw_inputs (doesn't work)self._raw_input_running = False
An unused leftover?
On Thu, 2013-04-04 at 14:54 +0200, Radek Vykydal wrote:
On 03/26/2013 02:51 PM, Vratislav Podzimek wrote:
@@ -91,6 +95,9 @@ class App(object): # ensure unique thread names self._in_thread_counter = 0
# do not run multiple raw_inputs (doesn't work)self._raw_input_running = FalseAn unused leftover?
Exactly, good catch, thanks!
There are many code paths depending on the state in which an exception appears. Some logging could be useful when searching for bugs. --- pyanaconda/exception.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/pyanaconda/exception.py b/pyanaconda/exception.py index 81f2a87..a24e809 100644 --- a/pyanaconda/exception.py +++ b/pyanaconda/exception.py @@ -80,6 +80,8 @@ class AnacondaExceptionHandler(ExceptionHandler):
"""
+ log.debug("running handleException") + ty = dump_info.exc_info.type
if issubclass(ty, blivet.errors.StorageError) and value.hardware_fault: @@ -105,21 +107,28 @@ class AnacondaExceptionHandler(ExceptionHandler): if Gtk.main_level() > 0: # main loop is running, don't crash it by running another one # potentially from a different thread + log.debug("Gtk running, queuing exception handler to the " + "main loop") GLib.idle_add(self.run_handleException, dump_info) else: - log.info("running handler") + log.debug("Gtk not running, starting Gtk and running " + "exception handler in it") super(AnacondaExceptionHandler, self).handleException( dump_info)
except RuntimeError: + log.debug("Gtk cannot be initialized") # X not running (Gtk cannot be initialized) if threadMgr.in_main_thread(): + log.debug("In the main thread, running exception handler") print "An unknown error has occured, look at the "\ "/tmp/anaconda-tb* file(s) for more details" # in the main thread, run exception handler super(AnacondaExceptionHandler, self).handleException( dump_info) else: + log.debug("In a non-main thread, sending a message with " + "exception data") # not in the main thread, just send message with exception # data and let message handler run the exception handler in # the main thread
There is the RescueInterface class an instance of which should be assigned to anaconda.intf as in both other cases -- tui and gui. Also the 'meh_interface' and 'tty_num' properties have to added to make RescueInterface valid user interface with exception handling support. But since RescueInterface uses snack, it unfortunately needs a special class for exception handling interface that shuts snack down before running textual exception handler. --- anaconda | 8 +++++--- pyanaconda/rescue.py | 25 ++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/anaconda b/anaconda index 0d60c71..c6e5c92 100755 --- a/anaconda +++ b/anaconda @@ -959,6 +959,10 @@ if __name__ == "__main__": # now start the interface setupDisplay(anaconda, opts, addon_paths)
+ if anaconda.rescue: + from pyanaconda import rescue + anaconda.intf = rescue.RescueInterface() + # Set flag to prompt for missing ks data if anaconda.displayMode == 'c': flags.ksprompt = False @@ -1004,9 +1008,7 @@ if __name__ == "__main__": from pyanaconda.network import networkInitialize, wait_for_connecting_NM_thread
if anaconda.rescue: - from pyanaconda.rescue import doRescue - doRescue(anaconda.rescue_mount, ksdata) - + rescue.doRescue(anaconda.intf, anaconda.rescue_mount, ksdata)
networkInitialize(ksdata) if not flags.dirInstall: diff --git a/pyanaconda/rescue.py b/pyanaconda/rescue.py index c6750bf..cf3db7d 100644 --- a/pyanaconda/rescue.py +++ b/pyanaconda/rescue.py @@ -38,6 +38,7 @@ import re import network import subprocess from pykickstart.constants import * +import meh.ui.text
import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -97,6 +98,14 @@ class RescueInterface(InstallInterfaceBase): w.pop() return passphrase
+ @property + def meh_interface(self): + return self._meh_interface + + @property + def tty_num(self): + return 1 + def shutdown (self): self.screen.finish()
@@ -109,6 +118,18 @@ class RescueInterface(InstallInterfaceBase): def __init__(self): InstallInterfaceBase.__init__(self) self.screen = SnackScreen() + self._meh_interface = RescueExceptionHandlingIface(self.screen) + +class RescueExceptionHandlingIface(meh.ui.text.TextIntf): + def __init__(self, snack_screen, *args, **kwargs): + meh.ui.text.TextIntf.__init__(self, *args, **kwargs) + self._snack_running = True + self._snack_screen = snack_screen + + def __getattr__(self, attr): + if self._snack_running: + self._snack_screen.finish() + self._snack_running = False
def makeFStab(instPath = ""): if os.access("/proc/mounts", os.R_OK): @@ -191,7 +212,7 @@ def runShell(screen = None, msg=""): if screen: screen.finish()
-def doRescue(rescue_mount, ksdata): +def doRescue(intf, rescue_mount, ksdata): import blivet
for file in [ "services", "protocols", "group", "joe", "man.config", @@ -201,8 +222,6 @@ def doRescue(rescue_mount, ksdata): except OSError: pass
- intf = RescueInterface() - # Early shell access with no disk access attempts if not rescue_mount: # the %post should be responsible for mounting all needed file systems
This one is for rhbz#926913.
Ack.
----- Original Message -----
There is the RescueInterface class an instance of which should be assigned to anaconda.intf as in both other cases -- tui and gui. Also the 'meh_interface' and 'tty_num' properties have to added to make RescueInterface valid user interface with exception handling support. But since RescueInterface uses snack, it unfortunately needs a special class for exception handling interface that shuts snack down before running textual exception handler.
anaconda | 8 +++++--- pyanaconda/rescue.py | 25 ++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/anaconda b/anaconda index 0d60c71..c6e5c92 100755 --- a/anaconda +++ b/anaconda @@ -959,6 +959,10 @@ if __name__ == "__main__": # now start the interface setupDisplay(anaconda, opts, addon_paths)
- if anaconda.rescue:
from pyanaconda import rescueanaconda.intf = rescue.RescueInterface()- # Set flag to prompt for missing ks data if anaconda.displayMode == 'c': flags.ksprompt = False
@@ -1004,9 +1008,7 @@ if __name__ == "__main__": from pyanaconda.network import networkInitialize, wait_for_connecting_NM_thread
if anaconda.rescue:
from pyanaconda.rescue import doRescuedoRescue(anaconda.rescue_mount, ksdata)
rescue.doRescue(anaconda.intf, anaconda.rescue_mount,ksdata)
networkInitialize(ksdata) if not flags.dirInstall:diff --git a/pyanaconda/rescue.py b/pyanaconda/rescue.py index c6750bf..cf3db7d 100644 --- a/pyanaconda/rescue.py +++ b/pyanaconda/rescue.py @@ -38,6 +38,7 @@ import re import network import subprocess from pykickstart.constants import * +import meh.ui.text
import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -97,6 +98,14 @@ class RescueInterface(InstallInterfaceBase): w.pop() return passphrase
- @property
- def meh_interface(self):
return self._meh_interface- @property
- def tty_num(self):
return 1- def shutdown (self): self.screen.finish()
@@ -109,6 +118,18 @@ class RescueInterface(InstallInterfaceBase): def __init__(self): InstallInterfaceBase.__init__(self) self.screen = SnackScreen()
self._meh_interface =RescueExceptionHandlingIface(self.screen)
+class RescueExceptionHandlingIface(meh.ui.text.TextIntf):
- def __init__(self, snack_screen, *args, **kwargs):
meh.ui.text.TextIntf.__init__(self, *args, **kwargs)self._snack_running = Trueself._snack_screen = snack_screen- def __getattr__(self, attr):
if self._snack_running:self._snack_screen.finish()self._snack_running = Falsedef makeFStab(instPath = ""): if os.access("/proc/mounts", os.R_OK): @@ -191,7 +212,7 @@ def runShell(screen = None, msg=""): if screen: screen.finish()
-def doRescue(rescue_mount, ksdata): +def doRescue(intf, rescue_mount, ksdata): import blivet
for file in [ "services", "protocols", "group", "joe", "man.config",@@ -201,8 +222,6 @@ def doRescue(rescue_mount, ksdata): except OSError: pass
- intf = RescueInterface()
- # Early shell access with no disk access attempts if not rescue_mount: # the %post should be responsible for mounting all needed file systems
-- 1.7.11.7
anaconda-patches mailing list anaconda-patches@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/anaconda-patches
This is needed for Anaconda's text mode that has its own raw_input function that has to be used also by python-meh. And it will come handy when writing automated tests for python-meh's text interface.
Signed-off-by: Vratislav Podzimek vpodzime@redhat.com --- meh/ui/text.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 16 deletions(-)
diff --git a/meh/ui/text.py b/meh/ui/text.py index 1b1e297..9bed60a 100644 --- a/meh/ui/text.py +++ b/meh/ui/text.py @@ -17,6 +17,8 @@ # Author(s): Chris Lumens clumens@redhat.com # Vratislav Podzimek vpodzime@redhat.com # +from __future__ import print_function + from meh import * from meh.ui import * import report @@ -28,10 +30,40 @@ import os import gettext _ = lambda x: gettext.ldgettext("python-meh", x)
+ +class IOHandler(object): + """ + Class that provides methods for input and output. Its instance is expected + to be passed to objects performing some I/O operations. + + """ + + def __init__(self, in_func=raw_input, out_func=print): + """ + Constructor for the IOhandler class. Arguments can be used to override + default I/O functions with the custom ones. + + @param in_func: input function similar to standard raw_input + @type in_func: str -> str + @param out_func: output function similar to standard print + @type out_func: str -> None + + """ + + self.in_func = in_func + self.out_func = out_func + + def print(self, msg=""): + self.out_func(msg) + + def raw_input(self, prompt): + return self.in_func(prompt) + class TextWindow(object): """Helper class providing some common methods needed by all text windows."""
def __init__(self, title, *args, **kwargs): + self._io = kwargs.get("io_handler", IOHandler()) self._title = title
# XXX: I know this must be really hard for translators, but there is no @@ -45,12 +77,12 @@ class TextWindow(object):
def _print_rule(self): rule = self._usable_width * "=" - print rule + self._io.print(rule)
def print_header(self): - print + self._io.print() self._print_rule() - print self._title + self._io.print(self._title) self._print_rule()
def destroy(self): @@ -61,6 +93,16 @@ class TextIntf(AbstractIntf): def __init__(self, *args, **kwargs): AbstractIntf.__init__(self, *args, **kwargs) self.screen = kwargs.get("screen", None) + self._io = kwargs.get("io_handler", IOHandler()) + + def set_io_handler(self, handler): + """ + Set different IO handler. + + @type handler: an instance of the IOHandler class + + """ + self._io = handler
def enableNetwork(self, *args, **kwargs): """Should be provided by the inheriting class.""" @@ -68,20 +110,24 @@ class TextIntf(AbstractIntf): return False
def exitWindow(self, title, message, *args, **kwargs): + kwargs["io_handler"] = self._io win = ExitWindow(title, message, *args, **kwargs) win.run() win.destroy()
def mainExceptionWindow(self, text, exnFile, *args, **kwargs): + kwargs["io_handler"] = self._io win = MainExceptionWindow(text, exnFile, *args, **kwargs) return win
def messageWindow(self, title, message, *args, **kwargs): + kwargs["io_handler"] = self._io win = MessageWindow(title, message, *args, **kwargs) win.run() win.destroy()
def saveExceptionWindow(self, signature, *args, **kwargs): + kwargs["io_handler"] = self._io win = SaveExceptionWindow(signature, *args, **kwargs) win.run() win.destroy() @@ -112,16 +158,16 @@ class MainExceptionWindow(TextWindow, AbstractMainExceptionWindow):
def run(self, *args, **kwargs): self.print_header() - print self._short_traceback - print _("What do you want to do now?") + self._io.print(self._short_traceback) + self._io.print(_("What do you want to do now?")) for (idx, item) in enumerate(self._menu_items): - print "%d) %s" % (idx + 1, item[0]) + self._io.print("%d) %s" % (idx + 1, item[0]))
ret = -1 num_menu_items = len(self._menu_items) - print + self._io.print() while not (0 < ret <= num_menu_items): - ret = raw_input(_("Please make your choice from above: ")) + ret = self._io.raw_input(_("Please make your choice from above: ")) try: ret = int(ret) except ValueError: @@ -137,9 +183,9 @@ class MessageWindow(TextWindow, AbstractMessageWindow):
def run(self, *args, **kwargs): self.print_header() - print self._text - print - raw_input(_("Hit ENTER to continue")) + self._io.print(self._text) + self._io.print() + self._io.raw_input(_("Hit ENTER to continue"))
class ExitWindow(MessageWindow): def __init__(self, title, text, *args, **kwargs): @@ -147,14 +193,15 @@ class ExitWindow(MessageWindow):
def run(self, *args, **kwargs): self.print_header() - print self._text - print + self._io.print(self._text) + self._io.print()
# self._no_answer may be non-ascii string (simple .upper() doesn't work) no_answer_upper = self._no_answer.decode("utf-8").upper().encode("utf-8") - answer = raw_input(_("Are you sure you want to exit? [%(yes)s/%(no)s]") % - { "yes": self._yes_answer, - "no": no_answer_upper }) + answer = self._io.raw_input(_( + "Are you sure you want to exit? [%(yes)s/%(no)s]") % + { "yes": self._yes_answer, + "no": no_answer_upper })
# no answer means accepting the default (self._no_answer) and the answer # is case insensitive (and may be non-ascii)
As far as I am able to grasp the logic of the area, the patches look good to me (except for minor remark on 1/3).
On 03/26/2013 02:51 PM, Vratislav Podzimek wrote:
These are patches that fix and enhance behaviour of exception handling in some specific cases.
PATCH 1/3 adds the code that moves exception handling in text mode to the main thread even if an exception appears in a non-main thread. This is needed for various reasons and it is implemented using the asynchronous message handling in text mode. The basic idea is described in the commit message. And it needs a patch for python-meh that is being proposed so that we can tell python-meh to use our raw_input instead of the standard one that doesn't work with threads.
PATCH 2/3 only adds some logging that becomes really handy in case exception handling doesn't work as expected.
PATCH 3/3 fixes the issue with the rescue mode. It adds some needed pieces to the RescueInterface, moves the creation of its instance to a better place and adds a class for exception handling interface correclty shutting down snack before running the exception handler.
Vratislav Podzimek (3): Run exception handling in the main thread also in TUI Add logging to exception handling Make exception handling in the rescue mode work
anaconda | 8 ++-- pyanaconda/exception.py | 85 ++++++++++++++++++++++++++---------- pyanaconda/rescue.py | 25 +++++++++-- pyanaconda/ui/tui/__init__.py | 27 +++++++++++- pyanaconda/ui/tui/simpleline/base.py | 36 +++++++++++---- 5 files changed, 141 insertions(+), 40 deletions(-)
anaconda-patches@lists.fedorahosted.org