[master 26/30] Add and use function that makes sure we work with strings (#1014220)

M4rtinK installerbot-noreply at redhat.com
Mon Jun 1 14:04:43 UTC 2015


From: Vratislav Podzimek <vpodzime at redhat.com>

This is useful in many places where we are never sure if we get str or bytes and
we need/want to work with str objects.

Also remove one related (previously) unused import and obsolete comment
& needless encoding.
---
 pyanaconda/iutil.py                  | 48 ++++++++++++++++++++++++++++--------
 pyanaconda/localization.py           |  3 +--
 pyanaconda/ui/tui/simpleline/base.py | 32 ++++++++++--------------
 3 files changed, 52 insertions(+), 31 deletions(-)

diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py
index 8b0f06a..1d50ab6 100644
--- a/pyanaconda/iutil.py
+++ b/pyanaconda/iutil.py
@@ -30,7 +30,6 @@
 # Used for ascii_lowercase, ascii_uppercase constants
 import string # pylint: disable=deprecated-module
 import tempfile
-import types
 import re
 from urllib import quote, unquote
 import gettext
@@ -1079,13 +1078,34 @@ def is_unsupported_hw():
         log.debug("Installing on Unsupported Hardware")
     return status
 
+def ensure_str(str_or_bytes, keep_none=True):
+    """
+    Returns a str instance for given string or None if requested to keep it.
+
+    :param str_or_bytes: string to be kept or converted to str type
+    :type str_or_bytes: str or bytes
+    :param bool keep_none: whether to keep None as it is or raise ValueError if
+                           None is passed
+    :raises ValueError: if applied on an object not being of type bytes nor str
+                        (nor NoneType if :param:`keep_none` is False)
+    """
+
+    if keep_none and str_or_bytes is None:
+        return None
+    elif isinstance(str_or_bytes, str):
+        return str_or_bytes
+    elif isinstance(str_or_bytes, bytes):
+        return str_or_bytes.decode(sys.getdefaultencoding())
+    else:
+        raise ValueError("str_or_bytes must be of type 'str' or 'bytes', not '%s'" % type(str_or_bytes))
+
 # Define translations between ASCII uppercase and lowercase for
 # locale-independent string conversions. The tables are 256-byte string used
 # with str.translate. If str.translate is used with a unicode string,
 # even if the string contains only 7-bit characters, str.translate will
 # raise a UnicodeDecodeError.
-_ASCIIupper_table = string.maketrans(string.ascii_lowercase, string.ascii_uppercase)
-_ASCIIlower_table = string.maketrans(string.ascii_uppercase, string.ascii_lowercase)
+_ASCIIlower_table = str.maketrans(string.ascii_uppercase, string.ascii_lowercase)
+_ASCIIupper_table = str.maketrans(string.ascii_lowercase, string.ascii_uppercase)
 
 def _toASCII(s):
     """Convert a unicode string to ASCII"""
@@ -1106,7 +1126,12 @@ def upperASCII(s):
     The returned string will contain only ASCII characters. This function is
     locale-independent.
     """
-    return string.translate(_toASCII(s), _ASCIIupper_table)
+
+    # XXX: Python 3 has str.maketrans() and bytes.maketrans() so we should
+    # ideally use one or the other depending on the type of 's'. But it turns
+    # out we expect this function to always return string even if given bytes.
+    s = ensure_str(s)
+    return str.translate(_toASCII(s), _ASCIIupper_table)
 
 def lowerASCII(s):
     """Convert a string to lowercase using only ASCII character definitions.
@@ -1114,7 +1139,12 @@ def lowerASCII(s):
     The returned string will contain only ASCII characters. This function is
     locale-independent.
     """
-    return string.translate(_toASCII(s), _ASCIIlower_table)
+
+    # XXX: Python 3 has str.maketrans() and bytes.maketrans() so we should
+    # ideally use one or the other depending on the type of 's'. But it turns
+    # out we expect this function to always return string even if given bytes.
+    s = ensure_str(s)
+    return str.translate(_toASCII(s), _ASCIIlower_table)
 
 def upcase_first_letter(text):
     """
@@ -1158,11 +1188,9 @@ def have_word_match(str1, str2):
         # non-empty string cannot be found in an empty string
         return False
 
-    # Convert both arguments to unicode if not already
-    if isinstance(str1, str):
-        str1 = str1.decode('utf-8')
-    if isinstance(str2, str):
-        str2 = str2.decode('utf-8')
+    # Convert both arguments to string if not already
+    str1 = ensure_str(str1)
+    str2 = ensure_str(str2)
 
     str1 = str1.lower()
     str1_words = str1.split()
diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py
index 9a28837..1613f48 100644
--- a/pyanaconda/localization.py
+++ b/pyanaconda/localization.py
@@ -397,8 +397,7 @@ def get_xlated_timezone(tz_spec_part):
     xlated = langtable.timezone_name(tz_spec_part, languageIdQuery=parts["language"],
                                      territoryIdQuery=parts.get("territory", ""),
                                      scriptIdQuery=parts.get("script", ""))
-
-    return xlated.encode("utf-8")
+    return xlated
 
 def write_language_configuration(lang, root):
     """
diff --git a/pyanaconda/ui/tui/simpleline/base.py b/pyanaconda/ui/tui/simpleline/base.py
index 211b60e..9fec160 100644
--- a/pyanaconda/ui/tui/simpleline/base.py
+++ b/pyanaconda/ui/tui/simpleline/base.py
@@ -72,7 +72,7 @@ def __init__(self, title, yes_or_no_question=None, width=80, queue_instance=None
                  quit_message=None):
         """
         :param title: application title for whenever we need to display app name
-        :type title: unicode
+        :type title: str
 
         :param yes_or_no_question: UIScreen object class used for Quit dialog
         :type yes_or_no_question: class UIScreen accepting additional message arg
@@ -428,7 +428,7 @@ def input(self, args, key):
         :type args: anything
 
         :param key: the string entered by user
-        :type key: unicode
+        :type key: str
 
         :return: True if key was processed, False if it was not recognized
         :rtype: True|False
@@ -582,11 +582,11 @@ def show_all(self):
                 w.render(self.app.width)
             if isinstance(w, Widget):
                 self._print_long_widget(w)
-            elif isinstance(w, types.StringType):
-                print(w.decode("utf-8"))
+            elif type(w) == bytes:
+                print(w)
             else:
-                # not a widget, just print its unicode representation
-                print(unicode(w))
+                # not a widget or string, just print its string representation
+                print(str(w))
     show = show_all
 
     def hide(self):
@@ -597,7 +597,7 @@ def input(self, args, key):
         """Method called to process input. If the input is not handled here, return it.
 
         :param key: input string to process
-        :type key: unicode
+        :type key: str
 
         :param args: optional argument passed from switch_screen calls
         :type args: anything
@@ -605,7 +605,7 @@ def input(self, args, key):
         :return: return True or INPUT_PROCESSED (None) if key was handled,
                  INPUT_DISCARDED (False) if the screen should not process input
                  on the App and key if you want it to.
-        :rtype: True|False|None|unicode
+        :rtype: True|False|None|str
         """
 
         return key
@@ -618,7 +618,7 @@ def prompt(self, args=None):
 
         :return: returns text to be shown next to the prompt for input or None
                  to skip further input processing
-        :rtype: unicode|None
+        :rtype: str|None
         """
         return _(u"  Please make your choice from above ['q' to quit | 'c' to continue |\n  'r' to refresh]: ")
 
@@ -684,10 +684,10 @@ def get_lines(self):
         """Get lines to write out in order to show this widget.
 
            :return: lines representing this widget
-           :rtype: list(unicode)
+           :rtype: list(str)
            """
 
-        return [unicode(u"".join(line)) for line in self._buffer]
+        return [str(u"".join(line)) for line in self._buffer]
 
     def setxy(self, row, col):
         """Sets cursor position.
@@ -755,7 +755,7 @@ def write(self, text, row=None, col=None, width=None, block=False):
         """This method emulates typing machine writing to this widget's buffer.
 
            :param text: text to type
-           :type text: unicode
+           :type text: str
 
            :param row: row number to start at (default is at the cursor position)
            :type row: int
@@ -772,13 +772,7 @@ def write(self, text, row=None, col=None, width=None, block=False):
         if not text:
             return
 
-        if isinstance(text, str):
-            try:
-                text = text.decode("utf-8")
-            except UnicodeDecodeError as e:
-                raise ValueError("Unable to decode string %s" %
-                                 str(e.object).decode("utf-8", "replace"))
-
+        text = iutil.ensure_str(text)
         if row is None:
             row = self._cursor[0]
 


-- 
To view this commit on github, visit https://github.com/rhinstaller/anaconda/commit/0f69039154ab2c1db5ed06ba8f76368483d3b246


More information about the anaconda-patches mailing list