[python-meh] [PATCH] Introduce support for Python 3 while keeping Python 2 working

Miro Hrončok miro at hroncok.cz
Tue Oct 15 14:30:12 UTC 2013


This commit introduces support for Python 3. Some of the behaviour is slightly
changed, however, it should work in general.

Libreport is still not working with Python 3, but that should be the only
blocking thing now.

Makefile and setup.py might need some modifications as well, currently only
make test3 has been added.
---
 Makefile                  |  4 ++++
 meh/__init__.py           |  4 ++--
 meh/dump.py               | 39 ++++++++++++++++++++++++++++-----------
 meh/handler.py            | 12 ++++++------
 meh/safe_string.py        |  7 +++++++
 tests/baseclass.py        |  7 +++----
 tests/handle_binary.py    | 15 +++++++++------
 tests/handle_unicode.py   | 13 ++++++++-----
 tests/safe_string_test.py | 15 ++++++++++-----
 9 files changed, 77 insertions(+), 39 deletions(-)

diff --git a/Makefile b/Makefile
index d0a9784..93d5b81 100644
--- a/Makefile
+++ b/Makefile
@@ -23,6 +23,10 @@ test:
 	@echo "*** Running unittests ***"
 	PYTHONPATH=. python $(TESTSUITE) -v
 
+test3:
+	@echo "*** Running unittests ***"
+	PYTHONPATH=. python3 $(TESTSUITE) -v
+
 install:
 	python setup.py install --root=$(DESTDIR)
 	$(MAKE) -C po install
diff --git a/meh/__init__.py b/meh/__init__.py
index c952527..6d554af 100644
--- a/meh/__init__.py
+++ b/meh/__init__.py
@@ -85,8 +85,8 @@ class Config(object):
 
         # Override the defaults set above with whatever's passed in as an
         # argument.  Unknown arguments get thrown away.
-        for (key, val) in kwargs.iteritems():
-            if self.__dict__.has_key(key):
+        for (key, val) in kwargs.items():
+            if key in self.__dict__:
                 self.__dict__[key] = val
 
         # Make sure required things are set.
diff --git a/meh/dump.py b/meh/dump.py
index 0a1e67b..761d5da 100644
--- a/meh/dump.py
+++ b/meh/dump.py
@@ -31,6 +31,17 @@ import codecs
 from meh import PackageInfo
 from meh.safe_string import SafeStr
 
+# Python 2/3 compatibilty
+try:
+    longtype = long
+except NameError:
+    longtype = int
+
+try:
+    unitype = unicode
+except NameError:
+    unitype = str
+
 class ExceptionDump(object):
     """This class represents a traceback and contains several useful methods
        for manipulating a traceback.  In general, clients should not have to
@@ -210,7 +221,7 @@ class ExceptionDump(object):
             """
 
             ret = list()
-            for (key, value) in os.environ.iteritems():
+            for (key, value) in os.environ.items():
                 ret.append("{0}={1}".format(key, value))
 
             return ret
@@ -286,9 +297,15 @@ class ExceptionDump(object):
         # out, and everything else will be assumed to be something that
         # needs to be recursed on.
         def __isSimpleType(instance):
-            return type(instance) in [types.BooleanType, types.ComplexType, types.FloatType,
-                                      types.IntType, types.LongType, types.NoneType,
-                                      types.StringType, types.UnicodeType] or \
+            return isinstance(instance, bool) or \
+                   isinstance(instance, complex) or \
+                   isinstance(instance, float) or \
+                   isinstance(instance, int) or \
+                   isinstance(instance, longtype) or \
+                   isinstance(instance, type(None)) or \
+                   isinstance(instance, bytes) or \
+                   isinstance(instance, str) or \
+                   isinstance(instance, unitype) or \
                    not hasattr(instance, "__class__") or \
                    not hasattr(instance, "__dict__")
 
@@ -298,7 +315,7 @@ class ExceptionDump(object):
         try:
             # Store the id(), not the instance name to protect against
             # instances that cannot be hashed.
-            if not self._dumpHash.has_key(id(instance)):
+            if not id(instance) in self._dumpHash:
                 self._dumpHash[id(instance)] = None
             else:
                 ret += "Already dumped (%s instance)\n" % instance.__class__.__name__
@@ -307,8 +324,8 @@ class ExceptionDump(object):
             ret += "Cannot dump object\n"
             return ret
 
-        if (instance.__class__.__dict__.has_key("__str__") or
-            instance.__class__.__dict__.has_key("__repr__")):
+        if ("__str__" in instance.__class__.__dict__ or
+            "__repr__" in instance.__class__.__dict__):
             try:
                 ret += "%s\n" % (instance,)
             except:
@@ -336,7 +353,7 @@ class ExceptionDump(object):
                 ret += "%s%s: Skipped\n" % (pad, curkey)
                 continue
 
-            if type(value) == types.ListType:
+            if isinstance(value, list):
                 ret += "%s%s: [" % (pad, curkey)
                 first = 1
                 for item in value:
@@ -350,7 +367,7 @@ class ExceptionDump(object):
                     else:
                         ret += self._dumpClass(item, level + 1, skipList=skipList)
                 ret += "]\n"
-            elif type(value) == types.DictType:
+            elif isinstance(value, dict):
                 # append things one after another so that e.g. binary data is
                 # replaced by hexa values separately
                 ret += pad
@@ -362,7 +379,7 @@ class ExceptionDump(object):
                         ret += ", "
                     else:
                         first = 0
-                    if type(k) == types.StringType:
+                    if isinstance(k, str) or isinstance(k, unitype):
                         ret += "'"
                         ret += k
                         ret += "': "
@@ -439,7 +456,7 @@ class ExceptionDump(object):
         # Filter out item names and callbacks that should appear
         # only as attachments
         items_callbacks = ((name, cb) for (name, (cb, attchmnt_only))
-                               in self.conf.callbackDict.iteritems()
+                               in self.conf.callbackDict.items()
                                if not attchmnt_only)
 
         # And now add data returned by the registered callbacks
diff --git a/meh/handler.py b/meh/handler.py
index 28b2183..b3708c2 100644
--- a/meh/handler.py
+++ b/meh/handler.py
@@ -19,7 +19,7 @@
 from meh import *
 import bdb
 import os
-from network import hasActiveNetDev
+from .network import hasActiveNetDev
 import signal
 import sys
 import report
@@ -191,9 +191,9 @@ class ExceptionHandler(object):
            :type exc_info: an instance of the meh.ExceptionInfo class
         """
 
-        print
-        print _("Use 'continue' command to quit the debugger and get back to "
-                "the main menu")
+        print("")
+        print(_("Use 'continue' command to quit the debugger and get back to "
+                "the main menu"))
         import pdb
         pdb.post_mortem(exc_info.stack)
         #no need to quit here, let's just get back to the main dialog
@@ -207,8 +207,8 @@ class ExceptionHandler(object):
 
         """
 
-        print
-        print _("Exit the shell to get back to the main menu")
+        print("")
+        print(_("Exit the shell to get back to the main menu"))
         proc = subprocess.Popen(["bash", "--login"], shell=True, cwd="/")
         proc.wait()
 
diff --git a/meh/safe_string.py b/meh/safe_string.py
index d4b13fb..a4cbb9f 100644
--- a/meh/safe_string.py
+++ b/meh/safe_string.py
@@ -19,6 +19,9 @@
 #
 #
 
+import sys
+PY = int(sys.version.split('.')[0])
+
 """
 This module provides a SafeStr class.
 
@@ -35,6 +38,10 @@ class SafeStr(str):
     """
 
     def __add__(self, other):
+
+        if PY > 2:
+            return SafeStr(str.__add__(self, str(other)))
+        
         if not (isinstance(other, str) or isinstance(other, unicode)):
             if hasattr(other, "__str__"):
                 other = other.__str__()
diff --git a/tests/baseclass.py b/tests/baseclass.py
index befa9b4..5fc5c36 100644
--- a/tests/baseclass.py
+++ b/tests/baseclass.py
@@ -1,5 +1,5 @@
 import glob
-import imputil
+import importlib
 import os
 import sys
 import tempfile
@@ -62,9 +62,8 @@ def loadModules(moduleDir, cls_pattern="_TestCase", skip_list=["__init__", "base
 
         # Attempt to load the found module.
         try:
-            found = imputil.imp.find_module(module)
-            loaded = imputil.imp.load_module(module, found[0], found[1], found[2])
-        except ImportError, e:
+            loaded = importlib.import_module(module)
+        except ImportError as e:
             print("Error loading module %s." % module)
             continue
 
diff --git a/tests/handle_binary.py b/tests/handle_binary.py
index d137136..fe35ed4 100644
--- a/tests/handle_binary.py
+++ b/tests/handle_binary.py
@@ -3,9 +3,9 @@
 from tests.baseclass import BaseTestCase
 from meh import Config
 
-BINARY_DATA = "\xff\x61\xfe\xdd"
-BINARY_DATA2 = "\xfe\x61\xff\xdd"
-BINARY_DATA3 = "\xfe\x62\xff\xdd"
+BINARY_DATA = b"\xff\x61\xfe\xdd"
+BINARY_DATA2 = b"\xfe\x61\xff\xdd"
+BINARY_DATA3 = b"\xfe\x62\xff\xdd"
 
 class BinaryExample(object):
     def __init__(self):
@@ -29,10 +29,13 @@ class HandleBinary_TestCase(BaseTestCase):
 
         # should contain the attribute name and hexa representation of binary
         # data ('\x61' == 'a' which shouldn't be translated)
-        self.assertIn("bin_data: \\xff\\x61\\xfe\\xdd\n", dump)
+        self.assertTrue("bin_data: \\xff\\x61\\xfe\\xdd\n" in dump or
+                        "bin_data: b'\\xffa\\xfe\\xdd'\n" in dump)
 
         # should contain the binary-keyed dict
-        self.assertIn("dict: {'\\xfe\\x61\\xff\\xdd': \\xfe\\x61\\xff\\xdd", dump)
+        self.assertTrue("dict: {'\\xfe\\x61\\xff\\xdd': \\xfe\\x61\\xff\\xdd" in dump or
+                        "dict: {b'\\xfea\\xff\\xdd': b'\\xfea\\xff\\xdd'" in dump)
 
         # should contain the list with binary item(s)
-        self.assertIn("list: [\\xfe\\x62\\xff\\xdd]", dump)
+        self.assertTrue("list: [\\xfe\\x62\\xff\\xdd]" in dump or
+                        "list: [b'\\xfeb\\xff\\xdd']" in dump)
diff --git a/tests/handle_unicode.py b/tests/handle_unicode.py
index adee2fa..971ead6 100644
--- a/tests/handle_unicode.py
+++ b/tests/handle_unicode.py
@@ -20,7 +20,10 @@ class HandleUnicode_TestCase(BaseTestCase):
     def setUp(self):
         # write UTF-8 and ASCII files for testing
         (fobj, self.uni_file_path) = self.openFile()
-        fobj.write(UNICODE_LINE.encode("utf-8"))
+        try:
+            fobj.write(UNICODE_LINE)
+        except UnicodeEncodeError:
+            fobj.write(UNICODE_LINE.encode("utf-8"))
         fobj.close()
 
         (fobj, self.ascii_file_path) = self.openFile()
@@ -37,10 +40,10 @@ class HandleUnicode_TestCase(BaseTestCase):
         # should not raise exception
         dump = self.dump(conf, unicode_example)
 
-        self.assertIn("unicode_str: " + UNICODE_STR.encode("utf-8"), dump)
-        self.assertIn("encoded_str: " + UNICODE_STR.encode("utf-8"), dump)
-        self.assertIn(UNICODE_LINE.encode("utf-8"), dump)
+        self.assertIn("_str: " + str(UNICODE_STR.encode("utf-8")), dump)
+        self.assertIn("encoded_str: " + str(UNICODE_STR.encode("utf-8")), dump)
+        self.assertIn(str(UNICODE_LINE.encode("utf-8")), dump)
 
         self.assertIn("ascii_str: " + ASCII_STR, dump)
-        self.assertIn(ASCII_LINE, dump)
+        self.assertIn(ASCII_LINE.rstrip(), dump)
 
diff --git a/tests/safe_string_test.py b/tests/safe_string_test.py
index 70e7695..e771afd 100755
--- a/tests/safe_string_test.py
+++ b/tests/safe_string_test.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 
 from tests.baseclass import BaseTestCase
-from meh.safe_string import SafeStr
+from meh.safe_string import SafeStr, PY
 
 class TestClass(object):
     def __str__(self):
@@ -16,7 +16,7 @@ class SafeStr_TestCase(BaseTestCase):
         self.unistr = u"ááááá"
         self.enc_unistr = self.unistr.encode("utf-8")
         self.asciistr = "aaaa"
-        self.bindata = '\xff\xff\xfe'
+        self.bindata = b'\xff\xff\xfe'
         self.test_object = TestClass()
         self.test_object2 = TestClass2()
 
@@ -30,10 +30,15 @@ class SafeStr_TestCase(BaseTestCase):
 
         self.assertIn(self.asciistr, self.safestr)
 
-        # should be included twice -- appended enc_unistr and unistr
-        self.assertIn(2*self.enc_unistr, self.safestr)
+        if PY == 2:
+            self.assertIn("OMITTED OBJECT WITHOUT __str__ METHOD", self.safestr)
+            # should be included twice -- appended enc_unistr and unistr
+            self.assertIn(str(self.enc_unistr), self.safestr)
+        else:
+            self.assertIn("<safe_string_test.TestClass2 object at ", self.safestr)
+            self.assertIn(str(self.enc_unistr), self.safestr)
+            self.assertIn(self.unistr, self.safestr)
 
         self.assertIn("\\xff\\xff\\xfe", self.safestr)
         self.assertIn(str(self.test_object), self.safestr)
-        self.assertIn("OMITTED OBJECT WITHOUT __str__ METHOD", self.safestr)
 
-- 
1.8.3.1



More information about the anaconda-patches mailing list