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

Vratislav Podzimek vpodzime at redhat.com
Mon Jun 17 10:16:36 UTC 2013


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)
+
-- 
1.7.11.7



More information about the anaconda-patches mailing list