[blivet master 2/3] Force __str__ to return str.

David Shea dshea at redhat.com
Wed Oct 15 15:32:38 UTC 2014


Explicitly cast __str__ results to str any time there is an ambiguity
in type the result string may be. Add __unicode__ methods so that when
these objects are converted to unicode they don't need to go through
encoding conversions.
---
 blivet/deviceaction.py   | 10 +++++++++-
 blivet/devices/device.py |  9 ++++++++-
 blivet/partitioning.py   | 19 +++++++++++++++++--
 blivet/partspec.py       | 11 ++++++++++-
 blivet/size.py           | 10 +++++++++-
 blivet/util.py           | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
 blivet/zfcp.py           | 10 +++++++++-
 7 files changed, 108 insertions(+), 9 deletions(-)

diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py
index 8d0666b..337f3dd 100644
--- a/blivet/deviceaction.py
+++ b/blivet/deviceaction.py
@@ -248,7 +248,9 @@ class DeviceAction(util.ObjectID):
     def typeDesc(self):
         return _(self.typeDescStr)
 
-    def __str__(self):
+    # Force str and unicode types since there's a good chance that the self.device.*
+    # strings are unicode.
+    def _toString(self):
         s = "[%d] %s" % (self.id, self.typeDescStr)
         if self.isResize:
             s += " (%s)" % self.resizeString
@@ -258,6 +260,12 @@ class DeviceAction(util.ObjectID):
                                  self.device.id)
         return s
 
+    def __str__(self):
+        return util.stringize(self._toString())
+
+    def __unicode__(self):
+        return util.unicodeize(self._toString())
+
     def requires(self, action):
         """ Return True if self requires action. """
         return (not (self.isContainer or action.isContainer) and
diff --git a/blivet/devices/device.py b/blivet/devices/device.py
index 9e0d8d0..313abf8 100644
--- a/blivet/devices/device.py
+++ b/blivet/devices/device.py
@@ -110,10 +110,17 @@ class Device(util.ObjectID):
               "parents": pprint.pformat([str(p) for p in self.parents])})
         return s
 
-    def __str__(self):
+    # Force str and unicode types in case type or name is unicode
+    def _toString(self):
         s = "%s %s (%d)" % (self.type, self.name, self.id)
         return s
 
+    def __str__(self):
+        return util.stringize(self._toString())
+
+    def __unicode__(self):
+        return util.unicodeize(self._toString())
+
     def _addParent(self, parent):
         """ Called before adding a parent to this device.
 
diff --git a/blivet/partitioning.py b/blivet/partitioning.py
index 687d05b..7107fd3 100644
--- a/blivet/partitioning.py
+++ b/blivet/partitioning.py
@@ -33,6 +33,7 @@ from .formats import getFormat
 from .devicelibs.lvm import get_pool_padding
 from .size import Size
 from .i18n import _
+from .util import stringize, unicodeize
 
 import logging
 log = logging.getLogger("blivet")
@@ -1361,10 +1362,17 @@ class Chunk(object):
 
         return s
 
-    def __str__(self):
+    # Force str and unicode types in case path is unicode
+    def _toString(self):
         s = "%d on %s" % (self.length, self.path)
         return s
 
+    def __str__(self):
+        return stringize(self._toString())
+
+    def __unicode__(self):
+        return unicodeize(self._toString())
+
     def addRequest(self, req):
         """ Add a request to this chunk.
 
@@ -1586,11 +1594,18 @@ class DiskChunk(Chunk):
                "sectorSize": self.sectorSize})
         return s
 
-    def __str__(self):
+    # Force str and unicode types in case path is unicode
+    def _toString(self):
         s = "%d (%d-%d) on %s" % (self.length, self.geometry.start,
                                   self.geometry.end, self.path)
         return s
 
+    def __str__(self):
+        return stringize(self._toString())
+
+    def __unicode__(self):
+        return unicodeize(self._toString())
+
     def addRequest(self, req):
         """ Add a request to this chunk.
 
diff --git a/blivet/partspec.py b/blivet/partspec.py
index 129f574..164496b 100644
--- a/blivet/partspec.py
+++ b/blivet/partspec.py
@@ -19,6 +19,8 @@
 # Red Hat Author(s): Chris Lumens <clumens at redhat.com>
 #
 
+from .util import stringize, unicodeize
+
 class PartSpec(object):
     def __init__(self, mountpoint=None, fstype=None, size=None, maxSize=None,
                  grow=False, btr=False, lv=False, thin=False, weight=0,
@@ -64,7 +66,8 @@ class PartSpec(object):
         self.requiredSpace = requiredSpace
         self.encrypted = encrypted
 
-    def __str__(self):
+    # Force str and unicode types in case any of the properties are unicode
+    def _toString(self):
         s = ("%(type)s instance (%(id)s) -- \n"
              "  mountpoint = %(mountpoint)s  lv = %(lv)s"
              "  thin = %(thin)s  btrfs = %(btrfs)s\n"
@@ -77,3 +80,9 @@ class PartSpec(object):
               "thin": self.thin})
 
         return s
+
+    def __str__(self):
+        return stringize(self._toString())
+
+    def __unicode__(self):
+        return unicodeize(self._toString())
diff --git a/blivet/size.py b/blivet/size.py
index e5ef4e9..ec09bf0 100644
--- a/blivet/size.py
+++ b/blivet/size.py
@@ -32,6 +32,7 @@ import six
 
 from .errors import SizePlacesError
 from .i18n import _, N_
+from .util import stringize, unicodeize
 
 
 # Container for size unit prefix information
@@ -233,9 +234,16 @@ class Size(Decimal):
         self = Decimal.__new__(cls, value=size)
         return self
 
-    def __str__(self, eng=False, context=None):
+    # Force str and unicode types since the translated sizespec may be unicode
+    def _toString(self):
         return self.humanReadable()
 
+    def __str__(self, eng=False, context=None):
+        return stringize(self._toString())
+
+    def __unicode__(self):
+        return unicodeize(self._toString())
+
     def __repr__(self):
         return "Size('%s')" % self
 
diff --git a/blivet/util.py b/blivet/util.py
index 90b8515..fa62261 100644
--- a/blivet/util.py
+++ b/blivet/util.py
@@ -14,8 +14,6 @@ from contextlib import contextmanager
 
 import six
 
-from .size import Size
-
 import logging
 log = logging.getLogger("blivet")
 program_log = logging.getLogger("program")
@@ -144,6 +142,8 @@ def total_memory():
 
         :rtype: :class:`~.size.Size`
     """
+    from .size import Size
+
     lines = open("/proc/meminfo").readlines()
     for line in lines:
         if line.startswith("MemTotal:"):
@@ -324,6 +324,8 @@ def numeric_type(num):
         Return the number if the type is sensible or raise ValueError
         if not.
     """
+    from .size import Size
+
     if num is None:
         num = 0
     elif not isinstance(num, (six.integer_types, float, Size, Decimal)):
@@ -382,6 +384,48 @@ def canonicalize_UUID(a_uuid):
     """
     return str(uuid.UUID(a_uuid.replace(':', '')))
 
+# Most Python 2/3 compatibility code equates python 2 str with python 3 bytes,
+# but the equivalence that we actually need to avoid return type surprises is
+# str/str.
+def stringize(inputstr):
+    """ Convert strings to a format compatible with Python 2's str.
+
+        :param str inputstr: the string to convert
+
+        :returns: a string with the correct type
+        :rtype: str
+
+        This method is for use in __str__ calls to ensure that they always
+        return a str. In Python 3, this method simply inputstr as a string. In
+        Python 2, it converts unicode into str. The returned str in python 2 is
+        encoded using utf-8.
+    """
+    if six.PY2:
+        if isinstance(inputstr, unicode):
+            inputstr = inputstr.encode('utf-8')
+
+    return str(inputstr)
+
+# Like six.u, but without the part where it raises an exception on unicode
+# objects
+def unicodeize(inputstr):
+    """ Convert strings to a format compatible with Python 2's unicode.
+
+        :param str inputstr: the string to convert
+
+        :returns: a string with the correct type
+        :rtype: unicode
+
+        This method is for use in __unicode__ calls to ensure that they always
+        return a unicode. This method does not handle non-ASCII characters
+        in str parameters, but non-ASCII characters in unicode parameters will
+        be correctly passed through.
+    """
+    if six.PY2:
+        return unicode(inputstr)
+    else:
+        return str(inputstr)
+
 ##
 ## Convenience functions for examples and tests
 ##
diff --git a/blivet/zfcp.py b/blivet/zfcp.py
index 5036540..0b93e6f 100644
--- a/blivet/zfcp.py
+++ b/blivet/zfcp.py
@@ -24,6 +24,7 @@ import os
 from . import udev
 from . import util
 from .i18n import _
+from .util import stringize, unicodeize
 
 import logging
 log = logging.getLogger("blivet")
@@ -51,9 +52,16 @@ class ZFCPDevice:
         if not self.checkValidFCPLun(self.fcplun):
             raise ValueError(_("You have not specified a FCP LUN or the number is invalid."))
 
-    def __str__(self):
+    # Force str and unicode types in case any of the properties are unicode
+    def _toString(self):
         return "%s %s %s" %(self.devnum, self.wwpn, self.fcplun)
 
+    def __str__(self):
+        return stringize(self._toString())
+
+    def __unicode__(self):
+        return unicodeize(self._toString())
+
     def sanitizeDeviceInput(self, dev):
         if dev is None or dev == "":
             return None
-- 
2.1.0



More information about the anaconda-patches mailing list