Source

suds python 3 patches / encoding

Full commit
# HG changeset patch
# Parent 38c0c17174b5765fa80864b478e9a85491146042
encoding

diff --git a/suds/__init__.py b/suds/__init__.py
--- a/suds/__init__.py
+++ b/suds/__init__.py
@@ -160,5 +160,35 @@
     else:
         __str__ = lambda x: unicode(x).encode('utf-8')
 
+# Compatibility wrappers to convert between bytes and strings.
+if sys.version_info >= (3, 0):
+    def str2bytes(s):
+        if isinstance(s, bytes):
+            return s
+        return s.encode('latin1')
+    def bytes2str(s):
+        if isinstance(s, str):
+            return s
+        return s.decode('latin1')
+else:
+    # For Python 2 bytes and string types are the same.
+    str2bytes = lambda s: s
+    bytes2str = lambda s: s
+
+#   Quick-fix helper function for making some __str__ & __repr__ function
+# implementations originally returning UTF-8 encoded strings portable to Python
+# 3. The original implementation worked in Python 2 but in Python 3 this would
+# return a bytes object which is not an allowed return type for those calls. In
+# Python 3 on the other hand directly returning a unicode string from them is
+# perfectly valid and there is no need for converting those strings to utf-8
+# encoded strings in the first place.
+#   The original implementation classes should most likely be refactored to use
+# unicode for internal representation and convert to encoded bytes only at the
+# last possible moment, e.g. on an explicit __str__/__repr__ call.
+if sys.version_info >= (3, 0):
+    str_to_utf8_in_py2 = lambda str: str
+else:
+    str_to_utf8_in_py2 = lambda str: str.encode('utf-8')
+
 
 import client
diff --git a/suds/client.py b/suds/client.py
--- a/suds/client.py
+++ b/suds/client.py
@@ -613,7 +613,7 @@
         @rtype: I{builtin} or I{subclass of} L{Object}
         """
         result = None
-        location = self.location()
+        location = suds.bytes2str(self.location())
         binding = self.method.binding.input
         transport = self.options.transport
         retxml = self.options.retxml
diff --git a/suds/sax/parser.py b/suds/sax/parser.py
--- a/suds/sax/parser.py
+++ b/suds/sax/parser.py
@@ -26,6 +26,7 @@
 
 """
 
+import sys
 from logging import getLogger
 import suds.metrics
 from suds import *
@@ -36,7 +37,11 @@
 from suds.sax.attribute import Attribute
 from xml.sax import make_parser, InputSource, ContentHandler
 from xml.sax.handler import feature_external_ges
-from cStringIO import StringIO
+
+if sys.version_info < (3, 0):
+    from cStringIO import StringIO as BytesIO
+else:
+    from io import BytesIO
 
 log = getLogger(__name__)
 
@@ -132,7 +137,7 @@
             return handler.nodes[0]
         if string is not None:
             source = InputSource(None)
-            source.setByteStream(StringIO(string))
+            source.setByteStream(BytesIO(suds.str2bytes(string)))
             sax.parse(source)
             timer.stop()
             metrics.log.debug('%s\nsax duration: %s', string, timer)
diff --git a/suds/sudsobject.py b/suds/sudsobject.py
--- a/suds/sudsobject.py
+++ b/suds/sudsobject.py
@@ -94,7 +94,8 @@
     def subclass(cls, name, bases, dict={}):
         if not isinstance(bases, tuple):
             bases = (bases,)
-        name = name.encode('utf-8')
+        # name is type unicode in python 2 -> not accepted by the type()
+        name = str(name)  
         key = '.'.join((name, str(bases)))
         subclass = cls.cache.get(key)
         if subclass is None:
diff --git a/suds/xsd/schema.py b/suds/xsd/schema.py
--- a/suds/xsd/schema.py
+++ b/suds/xsd/schema.py
@@ -407,7 +407,7 @@
 
     def __repr__(self):
         myrep = '<%s tns="%s"/>' % (self.id, self.tns[1])
-        return myrep.encode('utf-8')
+        return str_to_utf8_in_py2(myrep)
 
     def __unicode__(self):
         return self.str()
diff --git a/suds/xsd/sxbase.py b/suds/xsd/sxbase.py
--- a/suds/xsd/sxbase.py
+++ b/suds/xsd/sxbase.py
@@ -484,7 +484,7 @@
             s.append(' %s="%s"' % (n, v))
         s.append(' />')
         myrep = ''.join(s)
-        return myrep.encode('utf-8')
+        return str_to_utf8_in_py2(myrep)
 
     def __len__(self):
         n = 0