commit d526669810e0dc0a454260d5081fc96e16fc9e13
Author: John Dennis <jdennis@redhat.com>
Date:   Mon Jun 25 16:26:24 2018 -0400

    Make Python scripts compatible with both Py2 and Py3
    
    During the build if the Python3 interpreter is used a number of
    scripts will fail because they were never ported from Py2 to Py3. In
    general we want Python code to be compatible with both Py2 and
    Py3. This patch brings the scripts up to date with Py3 but retains
    backwards compatibility with Py2 (specifically Py 2.7, the last Py2
    release).
    
    Examples of the required changes are:
    
    * Replace use of the built-in function file() with open().  file()
      does not exist in Py3, open works in both Py2 and Py3.  The code was
      also modified to use a file context manager (e.g. with open(xxx) as
      f:). This assures open files are properly closed when the code block
      using the file goes out of scope. This is a standard modern Python
      idiom.
    
    * Replace all use of the print keyword with the six.print_()
      function, which itself is an emulation of Py3's print function. Py3
      no longer has a print keyword, only a print() function.
    
    * The dict methods .keys(), .values(), .items() no longer return a
      list in Py3, instead they return a "view" object which is an
      iterator whose result is an unordered set. The most notable
      consequence is you cannot index the result of these functions like
      your could in Py2 (e.g. dict.keys()[0] will raise a run time
      exception).
    
    * Replace use of StringIO.StringIO and cStringIO with
      six.StringIO. Py3 no longer has cStringIO and the six variant
      handles the correct import.
    
    * Py3 no longer allows the "except xxx, variable" syntax, where
      variable appering after the comma is assigned the exception object,
      you must use the "as" keyword to perform the variable assignment
      (e.g. execpt xxx as variable)
    
    Note: the modifications in this patch are the minimum necessary to get
    the build to run with the Py3 interpreter. There are numerous other
    Python scripts in the repo which need Py3 porting as well but because
    they are not invoked during a build they will be updated in a
    subsequent patch.
    
    License: MIT
    Signed-off-by: John Dennis <jdennis@redhat.com>

diff --git a/bindings/python/examples/get_attributes_from_assertion.py b/bindings/python/examples/get_attributes_from_assertion.py
index 44ceb9e5..8f37a337 100644
--- a/bindings/python/examples/get_attributes_from_assertion.py
+++ b/bindings/python/examples/get_attributes_from_assertion.py
@@ -1,8 +1,10 @@
 # Example SP Python code to get attributes from an assertion
 
+from six import print_
+
 for attribute in assertion.attributeStatement[0].attribute:
     if attribute.name == lasso.SAML2_ATTRIBUTE_NAME_EPR:
         continue
-    print 'attribute : ' + attribute.name
+    print_('attribute : ' + attribute.name)
     for value in attribute.attributeValue:
-        print '  value : ' + value.any[0].content
+        print_('  value : ' + value.any[0].content)
diff --git a/bindings/python/tests/binding_tests.py b/bindings/python/tests/binding_tests.py
index 6d8e0dfa..54c3635f 100755
--- a/bindings/python/tests/binding_tests.py
+++ b/bindings/python/tests/binding_tests.py
@@ -311,8 +311,8 @@ class BindingTestCase(unittest.TestCase):
                    </samlp:Extensions>'''
         node = lasso.Node.newFromXmlNode(content)
         assert 'next_url' in node.any[1]
-        assert 'huhu' in node.attributes.keys()[0]
-        assert node.attributes.values()[0] == 'xxx'
+        assert '{https://www.entrouvert.com/}huhu' in node.attributes.keys()
+        assert 'xxx' in node.attributes.values()
         node.any = ('<zob>coin</zob>',)
         node.attributes = {'michou': 'zozo'}
         assert '<zob>coin</zob>' in node.dump()
diff --git a/bindings/python/tests/idwsf2_tests.py b/bindings/python/tests/idwsf2_tests.py
index 6f80c53d..4e47a4a1 100755
--- a/bindings/python/tests/idwsf2_tests.py
+++ b/bindings/python/tests/idwsf2_tests.py
@@ -27,7 +27,7 @@
 import os
 import unittest
 import sys
-from StringIO import StringIO
+from six import StringIO
 import logging
 
 logging.basicConfig()
@@ -310,11 +310,11 @@ class MetadataTestCase(IdWsf2TestCase):
         self.failUnless(idp_disco.request.svcMD[0].svcMDID is None)
         try:
             idp_disco.checkSecurityMechanism()
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         try:
             idp_disco.validateRequest()
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         self.failUnless(idp_disco.response is not None)
         self.failUnlessEqual(len(idp_disco.metadatas), 1)
@@ -391,16 +391,16 @@ class MetadataTestCase(IdWsf2TestCase):
         self.failUnless(idp_disco is not None)
         try:
             idp_disco.processRequestMsg(wsp_disco.msgBody)
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         self.failUnless(idp_disco.request is not None)
         try:
             idp_disco.checkSecurityMechanism()
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         try:
             idp_disco.failRequest(lasso.IDWSF2_DISCOVERY_STATUS_CODE_FAILED, lasso.IDWSF2_DISCOVERY_STATUS_CODE_FORBIDDEN)
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         self.failUnless(idp_disco.response is not None)
         self.failUnless(idp_disco.response.status is not None)
@@ -415,7 +415,7 @@ class MetadataTestCase(IdWsf2TestCase):
             wsp_disco.processResponseMsg(idp_disco.msgBody)
         except lasso.Idwsf2DiscoveryForbiddenError:
             pass
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
 
     def test03(self):
@@ -475,7 +475,7 @@ class MetadataTestCase(IdWsf2TestCase):
         self.failUnless(soap_envelope.getMessageId() is not None)
         try:
             idp_disco.checkSecurityMechanism()
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         # redirect
         interactionUrl = spInteractionUrl
@@ -488,7 +488,7 @@ class MetadataTestCase(IdWsf2TestCase):
         self.failUnless(response.detail.any[0].redirectURL.startswith(interactionUrl + '?transactionID='))
         try:
             idp_disco.buildResponseMsg()
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         self.failUnless(idp_disco.msgBody is not None)
 
@@ -500,7 +500,7 @@ class MetadataTestCase(IdWsf2TestCase):
             wsp_disco.processResponseMsg(idp_disco.msgBody)
         except lasso.WsfprofileRedirectRequestError:
             pass
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         response_envelope = wsp_disco.getSoapEnvelopeResponse()
         self.failUnless(response_envelope.sb2GetRedirectRequestUrl().startswith(interactionUrl + '?transactionID='))
@@ -527,11 +527,11 @@ class MetadataTestCase(IdWsf2TestCase):
         self.failUnless(idp_disco.request.svcMD[0].svcMDID is None)
         try:
             idp_disco.checkSecurityMechanism()
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         try:
             idp_disco.validateRequest()
-        except lasso.Error, e:
+        except lasso.Error as e:
             self.fail(e)
         self.failUnless(idp_disco.response is not None)
         self.failUnlessEqual(len(idp_disco.metadatas), 1)
diff --git a/lasso/build_strerror.py b/lasso/build_strerror.py
index fca59628..908638d5 100644
--- a/lasso/build_strerror.py
+++ b/lasso/build_strerror.py
@@ -1,42 +1,42 @@
 #! /usr/bin/env python
 
-from cStringIO import StringIO
 import glob
 import re
 import sys
 import os
+from six import print_, StringIO
 
 srcdir = sys.argv[1]
 
-hlines = file('%s/errors.h' % srcdir,'r').readlines()
 messages = dict()
 description = ''
 
-for line in hlines:
-    m = re.match(r'^ \* LASSO.*ERROR', line)
-    if m:
-        description = ''
-        continue
-    m = re.match(r'^ \* (.*[^:])$', line)
-    if m:
-        description += m.group(1)
-    m = re.match(r'#define (LASSO_\w*ERROR\w+)', line)
-    if m and description:
-        description = re.sub(r'[ \n]+', ' ', description).strip()
-        messages[m.group(1)] = description
-        description = ''
-    else:
-        m = re.match(r'#define (LASSO_\w*ERROR\w+)',line)
+with open('%s/errors.h' % srcdir,'r') as f:
+    for line in f:
+        m = re.match(r'^ \* LASSO.*ERROR', line)
         if m:
-            messages[m.group(1)] = m.group(1)
+            description = ''
+            continue
+        m = re.match(r'^ \* (.*[^:])$', line)
+        if m:
+            description += m.group(1)
+        m = re.match(r'#define (LASSO_\w*ERROR\w+)', line)
+        if m and description:
+            description = re.sub(r'[ \n]+', ' ', description).strip()
+            messages[m.group(1)] = description
+            description = ''
+        else:
+            m = re.match(r'#define (LASSO_\w*ERROR\w+)',line)
+            if m:
+                messages[m.group(1)] = m.group(1)
 
-clines = file('%s/errors.c.in' % srcdir,'r').readlines()
-for line in clines:
-    if '@ERROR_CASES@' in line:
-        keys = messages.keys()
-        keys.sort()
-        for k in keys:
-            print """		case %s:
-			return "%s";""" % (k,messages[k].rstrip('\n'))
-    else:
-        print line,
+with open('%s/errors.c.in' % srcdir,'r') as f:
+    for line in f:
+        if '@ERROR_CASES@' in line:
+            keys = sorted(messages.keys())
+            for k in keys:
+                print_('		case %s:\n'
+                       '			return "%s";' %
+                       (k,messages[k].rstrip('\n')))
+        else:
+            print_(line, end="")
