[PATCH] Add and use the safe_string module and the SafeStr class

Radek Vykydal rvykydal at redhat.com
Mon Jun 17 12:11:03 UTC 2013


Looks good to me.

On 06/17/2013 12:16 PM, Vratislav Podzimek wrote:
> safe_string module provides the 'SafeStr' class which is a class inheriting from
> Python's 'str' class and overriding the __add__ method to allow safe appending
> of ascii strings, utf-8 encoded unicode strings, unicode strings, binary data
> represented by a string and objects with or without __str__ method.
>
> With this class we no longer have to check values for being of type UnicodeType,
> and we can handle binary data correctly.
>
> Resolves: rhbz#886983
> Resolves: rhbz#974032
>
> Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
> ---
>   meh/dump.py               | 13 ++++-------
>   meh/safe_string.py        | 56 +++++++++++++++++++++++++++++++++++++++++++++++
>   tests/handle_binary.py    | 23 +++++++++++++++++++
>   tests/safe_string_test.py | 38 ++++++++++++++++++++++++++++++++
>   4 files changed, 121 insertions(+), 9 deletions(-)
>   create mode 100644 meh/safe_string.py
>   create mode 100644 tests/handle_binary.py
>   create mode 100755 tests/safe_string_test.py
>
> diff --git a/meh/dump.py b/meh/dump.py
> index cc049c0..f52c051 100644
> --- a/meh/dump.py
> +++ b/meh/dump.py
> @@ -30,6 +30,7 @@ import types
>   import sys
>   import codecs
>   from meh import PackageInfo
> +from meh.safe_string import SafeStr
>   
>   class ExceptionDump(object):
>       """This class represents a traceback and contains several useful methods
> @@ -292,7 +293,7 @@ class ExceptionDump(object):
>                      not hasattr(instance, "__class__") or \
>                      not hasattr(instance, "__dict__")
>   
> -        ret = ""
> +        ret = SafeStr()
>   
>           # protect from loops
>           try:
> @@ -360,21 +361,15 @@ class ExceptionDump(object):
>                           first = 0
>                       if type(k) == types.StringType:
>                           ret += "'%s': " % (k,)
> -                    elif type(k) == types.UnicodeType:
> -                        ret += "'%s': " % k.encode("utf-8")
>                       else:
>                           ret += "%s: " % (k,)
>   
>                       if __isSimpleType(v):
> -                        if type(v) == types.UnicodeType:
> -                            v = v.encode("utf-8")
>                           ret += "%s" % (v,)
>                       else:
>                           ret += self._dumpClass(v, level + 1, parentkey = curkey, skipList=skipList)
>                   ret += "}\n"
>               elif __isSimpleType(value):
> -                if type(value) == types.UnicodeType:
> -                    value = value.encode("utf-8")
>                   ret += "%s%s: %s\n" % (pad, curkey, value)
>               else:
>                   ret += "%s%s: " % (pad, curkey)
> @@ -392,7 +387,7 @@ class ExceptionDump(object):
>                     written out, except for those mentioned in the attrSkipList.
>           """
>           idSkipList = []
> -        ret = ""
> +        ret = SafeStr()
>   
>           # We need to augment the environment eval() will run in with the
>           # bindings that were local when the traceback happened so that the
> @@ -493,7 +488,7 @@ class ExceptionDump(object):
>   
>           """
>   
> -        ret = str(self)
> +        ret = SafeStr(str(self))
>           ret += self.dump(obj)
>   
>           return ret
> diff --git a/meh/safe_string.py b/meh/safe_string.py
> new file mode 100644
> index 0000000..558ebb7
> --- /dev/null
> +++ b/meh/safe_string.py
> @@ -0,0 +1,56 @@
> +#
> +# Copyright (C) 2013  Red Hat, Inc.
> +# All rights reserved.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +#
> +# Author: Vratislav Podzimek <vpodzime at redhat.com>
> +#
> +#
> +
> +"""
> +This module provides a SafeStr class.
> +
> + at see: SafeStr
> +
> +"""
> +
> +class SafeStr(str):
> +    """
> +    String class that has a modified __add__ method so that ascii strings,
> +    binary data represented as a byte string and unicode objects can be
> +    safely appended to it (not causing traceback). BINARY DATA IS OMITTED.
> +
> +    """
> +
> +    def __add__(self, other):
> +        if not (isinstance(other, str) or isinstance(other, unicode)):
> +            if hasattr(other, "__str__"):
> +                other = other.__str__()
> +            else:
> +                other = "OMITTED OBJECT WITHOUT __str__ METHOD"
> +
> +        if isinstance(other, unicode):
> +            ret = SafeStr(str.__add__(self, other.encode("utf-8")))
> +        else:
> +            try:
> +                # try to decode which doesn't cause traceback for utf-8 encoded
> +                # non-ascii string and ascii string
> +                other.decode("utf-8")
> +                ret = SafeStr(str.__add__(self, other))
> +            except UnicodeDecodeError:
> +                # binary data
> +                ret = SafeStr(str.__add__(self, "OMITTED BINARY DATA"))
> +
> +        return ret
> diff --git a/tests/handle_binary.py b/tests/handle_binary.py
> new file mode 100644
> index 0000000..50f44aa
> --- /dev/null
> +++ b/tests/handle_binary.py
> @@ -0,0 +1,23 @@
> +# -*- coding: utf-8 -*-
> +
> +from tests.baseclass import BaseTestCase
> +from meh import Config
> +
> +BINARY_DATA = "\xff\xfe\xdd"
> +
> +class BinaryExample(object):
> +    def __init__(self):
> +        self.bin_data = BINARY_DATA
> +
> +class HandleBinary_TestCase(BaseTestCase):
> +    def runTest(self):
> +        binary_example = BinaryExample()
> +
> +        conf = Config(programName="UnicodeTest",
> +                      programVersion="1.0")
> +
> +        # should not raise exception
> +        dump = self.dump(conf, binary_example)
> +
> +        self.assertIn("OMITTED BINARY DATA", dump)
> +
> diff --git a/tests/safe_string_test.py b/tests/safe_string_test.py
> new file mode 100755
> index 0000000..a41e289
> --- /dev/null
> +++ b/tests/safe_string_test.py
> @@ -0,0 +1,38 @@
> +# -*- coding: utf-8 -*-
> +
> +from tests.baseclass import BaseTestCase
> +from meh.safe_string import SafeStr
> +
> +class TestClass(object):
> +    def __str__(self):
> +        return "string representation of TestClass instance"
> +
> +class TestClass2():
> +    pass
> +
> +class SafeStr_TestCase(BaseTestCase):
> +    def setUp(self):
> +        self.safestr = SafeStr()
> +        self.unistr = u"ááááá"
> +        self.enc_unistr = self.unistr.encode("utf-8")
> +        self.asciistr = "aaaa"
> +        self.bindata = '\xff\xff\xfe'
> +        self.test_object = TestClass()
> +        self.test_object2 = TestClass2()
> +
> +    def runTest(self):
> +        self.safestr += self.asciistr
> +        self.safestr += self.enc_unistr
> +        self.safestr += self.unistr
> +        self.safestr += self.bindata
> +        self.safestr += self.test_object
> +        self.safestr += self.test_object2
> +
> +        self.assertIn(self.asciistr, self.safestr)
> +
> +        # should be included twice -- appended enc_unistr and unistr
> +        self.assertIn(2*self.enc_unistr, self.safestr)
> +        self.assertIn("OMITTED BINARY DATA", self.safestr)
> +        self.assertIn(str(self.test_object), self.safestr)
> +        self.assertIn("OMITTED OBJECT WITHOUT __str__ METHOD", self.safestr)
> +



More information about the anaconda-patches mailing list