[Kimchi-devel] [PATCH v2] kimchi.exception: Properly Decode All Kinds of Exception Arguments
Aline Manera
alinefm at linux.vnet.ibm.com
Thu Apr 3 13:21:38 UTC 2014
Reviewed-by: Aline Manera <alinefm at linux.vnet.ibm.com>
On 04/03/2014 01:01 AM, zhshzhou at linux.vnet.ibm.com wrote:
> From: Zhou Zheng Sheng <zhshzhou at linux.vnet.ibm.com>
>
> KimchiException provides the ability to translate each argument and the
> error message. It decodes the translated message to unicode. It's also
> smart and avoids decoding a unicode argument twice by a check to make
> sure the to-be-decoded value is not unicode.
>
> The problem is that when a KimchiException is initialized by an object
> that can format itself to a unicode sting, for example,
>
> try:
> ...
> except OperationFailed as e:
> ...
> raise OperationFailed('KCHXXXX', {'err': e})
>
> the variable "e" is not a unicode object so it passes the check
> mentioned above, then it gets decoded twice and raises an encoding
> exception.
>
> This patch does not only rely on the type of the to-be-decoded object,
> but also catches the encoding exeption. It firstly tries to call str(obj)
> to get the ascii string of the obj, then decodes it to unicode. This is
> valid for a normal string or any object that formats itself to a normal
> string. If it fails, it tries to call unicode(obj) to get the unicode
> string of the object. This is valid for objects that formats themselves
> to a unicode string. In all, it handles various objects like following.
>
> If obj is a unicode string, use it directly.
> If obj is a normal string, decode it to a unicode string.
> If obj can format itself to a normal string, format it and decode it to
> unicode.
> If obj can format itself to a unicode string, format it.
>
> Another problem is that by the current design, KimchiException always
> stores error message in unicode. This can cause encoding exception when
> we call "str(KimchiExceptionObject)". This patch is not a total refactor
> of the translation and encoding so it does not solve this problem. The
> workaround is using KimchiExceptionObject.message instead of str it.
>
> Notice: I'm following the Python str.encode and decode semantics in the
> commit message.
> "encode" means converting unicode sting to ascii/utf-8.
> "decode" means converting ascii/utf-8 string to unicode.
> "encoding" means the action of decode and encode in general.
>
> v2
> sosreport_generate() function in debugreport code encapsules an
> OperationFailed in another OperationFailed and re-raise. This leads to
> odd error message like follow:
> KCHDR0005E: Unable to generate debug report my-debugreport. Details:
> KCHDR0003E: Unable to create debug report my-debugreport. Details: -9.
>
> The v2 patch excepts KimchiException and re-raise it directly to
> produce a clean error message. For the rest vast kinds of exceptions,
> it still translates it to OperationFailed and encapsules it. As a
> result, all error recognized by Kimchi produces a clean error message
> with specific KCHXXXX error code. And unknown error produces an error
> message with KCHDR0005E error code following by the detailed exception.
>
> Though the change in v2 solves a root cause and does not make use of the
> KimchiException decoding improvements, the author still keeps the
> decoding code to make KimchiException more robust to all kinds of
> parameters in general.
>
> Signed-off-by: Zhou Zheng Sheng <zhshzhou at linux.vnet.ibm.com>
> ---
> src/kimchi/asynctask.py | 2 +-
> src/kimchi/exception.py | 8 +++++++-
> src/kimchi/model/debugreports.py | 16 ++++++++++++----
> 3 files changed, 20 insertions(+), 6 deletions(-)
>
> diff --git a/src/kimchi/asynctask.py b/src/kimchi/asynctask.py
> index b54f143..9805f33 100644
> --- a/src/kimchi/asynctask.py
> +++ b/src/kimchi/asynctask.py
> @@ -70,4 +70,4 @@ class AsyncTask(object):
> except Exception, e:
> cherrypy.log.error_log.error("Error in async_task %s " % self.id)
> cherrypy.log.error_log.error(traceback.format_exc())
> - cb("Unexpected exception: %s" % str(e), False)
> + cb("Unexpected exception: %s" % e.message, False)
> diff --git a/src/kimchi/exception.py b/src/kimchi/exception.py
> index 87982ea..fcf60cc 100644
> --- a/src/kimchi/exception.py
> +++ b/src/kimchi/exception.py
> @@ -54,7 +54,13 @@ class KimchiException(Exception):
>
> for key, value in args.iteritems():
> if not isinstance(value, unicode):
> - args[key] = unicode(str(value), 'utf-8')
> + try:
> + # In case the value formats itself to an ascii string.
> + args[key] = unicode(str(value), 'utf-8')
> + except UnicodeEncodeError:
> + # In case the value is a KimchiException or it formats
> + # itself to a unicode string.
> + args[key] = unicode(value)
>
> return unicode(translation.gettext(text), 'utf-8') % args
>
> diff --git a/src/kimchi/model/debugreports.py b/src/kimchi/model/debugreports.py
> index 66e4ef5..cd31b31 100644
> --- a/src/kimchi/model/debugreports.py
> +++ b/src/kimchi/model/debugreports.py
> @@ -26,7 +26,7 @@ import subprocess
> import time
>
> from kimchi import config
> -from kimchi.exception import NotFoundError, OperationFailed
> +from kimchi.exception import KimchiException, NotFoundError, OperationFailed
> from kimchi.model.tasks import TaskModel
> from kimchi.utils import add_task, kimchi_log
> from kimchi.utils import run_command
> @@ -64,6 +64,10 @@ class DebugReportsModel(object):
>
> @staticmethod
> def sosreport_generate(cb, name):
> + def log_error(e):
> + log = logging.getLogger('Model')
> + log.warning('Exception in generating debug file: %s', e)
> +
> try:
> command = ['sosreport', '--batch', '--name=%s' % name]
> output, error, retcode = run_command(command)
> @@ -109,15 +113,19 @@ class DebugReportsModel(object):
> cb('OK', True)
> return
>
> - except OSError:
> + except KimchiException as e:
> + log_error(e)
> + raise
> +
> + except OSError as e:
> + log_error(e)
> raise
>
> except Exception, e:
> # No need to call cb to update the task status here.
> # The task object will catch the exception raised here
> # and update the task status there
> - log = logging.getLogger('Model')
> - log.warning('Exception in generating debug file: %s', e)
> + log_error(e)
> raise OperationFailed("KCHDR0005E", {'name': name, 'err': e})
>
> @staticmethod
More information about the Kimchi-devel
mailing list