diff options
author | Dylan Baker <baker.dylan.c@gmail.com> | 2015-08-13 14:25:37 -0700 |
---|---|---|
committer | Dylan Baker <baker.dylan.c@gmail.com> | 2015-09-22 14:45:48 -0700 |
commit | 005502819fcb411c9f84b73408c7bc65488f7b03 (patch) | |
tree | 2e982fc934b7e589002009cc349d2b62748f4bd5 /framework/results.py | |
parent | 269bf51c9f08ad90bef38bf6c09b67d095f35d4f (diff) |
framework: move unicode conversion handling to TestResult
This implements a python descriptor pattern to handle setting the out
and err attributes, which should always be unicode.
The familiar "property" decorator is an example of a descriptor,
although it is a more terse way of doing it. And while that could have
been done here, by not making it a property we can reuse the exact same
class for both out and err, and could theoretically use it for other
attributes.
Signed-off-by: Dylan Baker <dylanx.c.baker@intel.com>
Diffstat (limited to 'framework/results.py')
-rw-r--r-- | framework/results.py | 49 |
1 files changed, 44 insertions, 5 deletions
diff --git a/framework/results.py b/framework/results.py index 40f750cf0..2753fd580 100644 --- a/framework/results.py +++ b/framework/results.py @@ -50,15 +50,43 @@ class Subtests(dict): return res +class StringDescriptor(object): # pylint: disable=too-few-public-methods + """A Shared data descriptor class for TestResult. + + This provides a property that can be passed a str or unicode, but always + returns a unicode object. + + """ + def __init__(self, name, default=unicode()): + assert isinstance(default, unicode) + self.__name = name + self.__default = default + + def __get__(self, instance, cls): + return getattr(instance, self.__name, self.__default) + + def __set__(self, instance, value): + if isinstance(value, str): + setattr(instance, self.__name, value.decode('utf-8', 'replace')) + elif isinstance(value, unicode): + setattr(instance, self.__name, value) + else: + raise TypeError('{} attribute must be a str or unicode instance, ' + 'but was {}.'.format(self.__name, type(value))) + + def __delete__(self, instance): + raise NotImplementedError + + class TestResult(object): """An object represting the result of a single test.""" - __slots__ = ['returncode', 'err', 'out', 'time', 'command', 'environment', - 'subtests', 'dmesg', '__result', 'images', 'traceback'] + __slots__ = ['returncode', '_err', '_out', 'time', 'command', 'traceback', + 'environment', 'subtests', 'dmesg', '__result', 'images'] + err = StringDescriptor('_err') + out = StringDescriptor('_out') def __init__(self, result=None): self.returncode = None - self.err = str() - self.out = str() self.time = float() self.command = str() self.environment = str() @@ -113,10 +141,14 @@ class TestResult(object): status.Status object """ + # pylint will say that assining to inst.out or inst.err is a non-slot + # because self.err and self.out are descriptors, methods that act like + # variables. Just silence pylint + # pylint: disable=assigning-non-slot inst = cls() # TODO: There's probably a more clever way to do this - for each in ['returncode', 'err', 'out', 'time', 'command', + for each in ['returncode', 'time', 'command', 'environment', 'result', 'dmesg']: if each in dict_: setattr(inst, each, dict_[each]) @@ -125,6 +157,13 @@ class TestResult(object): for name, value in dict_['subtests'].iteritems(): inst.subtests[name] = value + # out and err must be set manually to avoid replacing the setter + if 'out' in dict_: + inst.out = dict_['out'] + + if 'err' in dict_: + inst.err = dict_['err'] + return inst def update(self, dict_): |