Blame SOURCES/python3.patch

e965a0
diff -u a/src/python/daemonize.py b/src/python/new/daemonize.py
e965a0
--- a/src/python/daemonize.py	2017-10-17 23:11:48.000000000 +0200
e965a0
+++ b/src/python/new/daemonize.py	2018-06-25 21:06:09.000000000 +0200
e965a0
@@ -1,4 +1,4 @@
e965a0
-#!/usr/bin/env python
e965a0
+#!/usr/bin/python3
e965a0
 
e965a0
 # Authors:  Jiri Jaburek  <jjaburek@redhat.com>
e965a0
 #
e965a0
@@ -18,6 +18,7 @@
e965a0
 # along with this program; if not, write to the Free Software
e965a0
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
e965a0
 
e965a0
+from __future__ import print_function
e965a0
 import os, sys
e965a0
 
e965a0
 from pwd import getpwnam
e965a0
@@ -96,8 +97,8 @@
e965a0
                 # with original stderr (in case of errors), but with new uid/gid
e965a0
                 if ioredir:
e965a0
                     os.open(ioredir[0], os.O_RDWR)
e965a0
-                    os.open(ioredir[1], os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0666)
e965a0
-                    os.open(ioredir[2], os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0666)
e965a0
+                    os.open(ioredir[1], os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0o666)
e965a0
+                    os.open(ioredir[2], os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0o666)
e965a0
 
e965a0
                 os.umask(0)
e965a0
 
e965a0
@@ -116,7 +117,7 @@
e965a0
 
e965a0
 # argument parsing
e965a0
 def error(msg):
e965a0
-    print >> sys.stderr, "error: " + str(msg)
e965a0
+    print("error: " + str(msg), file=sys.stderr)
e965a0
     sys.exit(1)
e965a0
 
e965a0
 parser = OptionParser(usage='%prog [options] COMMAND')
e965a0
diff -u a/src/python/journal-compare.py b/src/python/new/journal-compare.py
e965a0
--- a/src/python/journal-compare.py	2018-06-25 21:01:54.490910141 +0200
e965a0
+++ b/src/python/new/journal-compare.py	2018-06-25 21:06:13.000000000 +0200
e965a0
@@ -1,6 +1,6 @@
e965a0
-#!/usr/bin/python2
e965a0
+#!/usr/bin/python3
e965a0
 
e965a0
-# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material 
e965a0
+# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material
e965a0
 # is made available to anyone wishing to use, modify, copy, or
e965a0
 # redistribute it subject to the terms and conditions of the GNU General
e965a0
 # Public License v.2.
e965a0
@@ -15,6 +15,7 @@
e965a0
 #
e965a0
 # Author: Petr Muller <pmuller@redhat.com>
e965a0
 
e965a0
+from __future__ import print_function
e965a0
 import xml.dom.minidom
e965a0
 import sys
e965a0
 
e965a0
@@ -125,9 +126,9 @@
e965a0
 		self.results = {}
e965a0
 
e965a0
 	def addTestResult(self, name, result):
e965a0
-		if not self.results.has_key(name):
e965a0
-			self.results[name] = Test(name)
e965a0
-		self.results[name].addResult(result)
e965a0
+                if name not in self.results:
e965a0
+                        self.results[name] = Test(name)
e965a0
+                self.results[name].addResult(result)
e965a0
 
e965a0
 	def compare(self, other):
e965a0
 		result_list = []
e965a0
@@ -135,7 +136,7 @@
e965a0
 			try:
e965a0
 				result_list.append(self.results[key].compare(other.results[key]))
e965a0
 			except KeyError:
e965a0
-				print "[WARN] Could not find corresponding test for: %s" % key
e965a0
+				print("[WARN] Could not find corresponding test for: %s" % key)
e965a0
 		return result_list
e965a0
 
e965a0
 try:
e965a0
@@ -161,7 +162,7 @@
e965a0
 	new_type, new_name = new_phases[i].getAttribute("type"), new_phases[i].getAttribute("name")
e965a0
 
e965a0
 	if old_type == new_type and old_name == new_name:
e965a0
-		print "Types match, so we are comparing phase %s of type %s" % (old_type, new_type)
e965a0
+		print( "Types match, so we are comparing phase %s of type %s" % (old_type, new_type))
e965a0
 		old_tests = TestSet()
e965a0
 		new_tests = TestSet()
e965a0
 		old_metrics = {}
e965a0
@@ -179,20 +180,20 @@
e965a0
 				tolerance = float(metric.getAttribute("tolerance"))
e965a0
 				metrics[key] = Metric(key, value, metric.getAttribute("type"), tolerance)
e965a0
 
e965a0
-		print "==== Actual compare ===="
e965a0
-		print " * Metrics * "
e965a0
+		print("==== Actual compare ====")
e965a0
+		print(" * Metrics * ")
e965a0
 		metric_results = []
e965a0
 		for key in old_metrics.keys():
e965a0
 			metric_results.append(old_metrics[key].compare(new_metrics[key]))
e965a0
 		for metric in metric_results:
e965a0
 			for message in metric.messages:
e965a0
-				print "[%s] %s (%s)" % (metric.result, metric.name, message)
e965a0
-		print " * Tests * "
e965a0
+				print("[%s] %s (%s)" % (metric.result, metric.name, message))
e965a0
+		print(" * Tests * ")
e965a0
 		test_results = old_tests.compare(new_tests)
e965a0
 		for test in test_results:
e965a0
-			print "[%s] %s" % (test.result, test.name)
e965a0
+			print("[%s] %s" % (test.result, test.name))
e965a0
 			for message in test.messages:
e965a0
-				print "\t - %s" % message
e965a0
+				print("\t - %s" % message)
e965a0
 
e965a0
 	else:
e965a0
-		print "We are not doing any compare, types dont match"
e965a0
+		print("We are not doing any compare, types dont match")
e965a0
diff -u a/src/python/journalling.py b/src/python/new/journalling.py
e965a0
--- a/src/python/journalling.py	2018-06-25 21:01:54.490910141 +0200
e965a0
+++ b/src/python/new/journalling.py	2018-06-25 21:06:19.000000000 +0200
e965a0
@@ -1,4 +1,4 @@
e965a0
-#!/usr/bin/python2
e965a0
+#!/usr/bin/python3
e965a0
 
e965a0
 # Authors:  Jakub Heger        <jheger@redhat.com>
e965a0
 #           Dalibor Pospisil   <dapospis@redhat.com>
e965a0
@@ -20,16 +20,17 @@
e965a0
 # along with this program; if not, write to the Free Software
e965a0
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
e965a0
 
e965a0
+# TODO fix xml pretty print
e965a0
+
e965a0
 
e965a0
-import sys
e965a0
 import os
e965a0
-import time
e965a0
 import re
e965a0
-from optparse import OptionParser
e965a0
-from lxml import etree
e965a0
+import sys
e965a0
+import six
e965a0
+import time
e965a0
 import base64
e965a0
-
e965a0
-# TODO fix xml pretty print
e965a0
+from lxml import etree
e965a0
+from optparse import OptionParser
e965a0
 
e965a0
 
e965a0
 xmlForbidden = [0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20,
e965a0
@@ -51,18 +52,19 @@
e965a0
         return self.items[-1]
e965a0
 
e965a0
 
e965a0
+# Saves the XML journal to a file.
e965a0
 def saveJournal(journal, journal_path):
e965a0
     try:
e965a0
         output = open(journal_path, 'wb')
e965a0
         output.write(etree.tostring(journal, xml_declaration=True, encoding='utf-8', pretty_print=True))
e965a0
         output.close()
e965a0
         return 0
e965a0
-    except IOError, e:
e965a0
+    except IOError as e:
e965a0
         sys.stderr.write('Failed to save journal to %s: %s' % (journal_path, str(e)))
e965a0
         return 1
e965a0
 
e965a0
 
e965a0
-# Adds attributes starttime and endtime to a element
e965a0
+# Adds attributes starttime and endtime to a element.
e965a0
 def addStartEndTime(element, starttime, endtime):
e965a0
     element.set("starttime", starttime)
e965a0
     element.set("endtime", endtime)
e965a0
@@ -72,7 +74,7 @@
e965a0
     return 0
e965a0
 
e965a0
 
e965a0
-# Find first and last timestamp to fill in starttime and endtime elements of given element
e965a0
+# Find first and last timestamp to fill in starttime and endtime attributes of given element.
e965a0
 def getStartEndTime(element):
e965a0
     starttime = ""
e965a0
     endtime = ""
e965a0
@@ -87,7 +89,7 @@
e965a0
 
e965a0
 # Parses and decodes lines given to it
e965a0
 # Returns number of spaces before element, name of the element,
e965a0
-# its attributes in a dictionary, and content of the element
e965a0
+# its attributes in a dictionary, and content of the element.
e965a0
 def parseLine(line):
e965a0
     TIME_FORMAT = "%Y-%m-%d %H:%M:%S %Z"
e965a0
     CONTENT_FLAG = 0
e965a0
@@ -99,12 +101,12 @@
e965a0
     # Count number of leading spaces
e965a0
     indent = len(line) - len(line.lstrip())
e965a0
 
e965a0
-    # splitting the line into list
e965a0
+    # Splitting the line into a list
e965a0
     splitted = line.split()
e965a0
 
e965a0
-    # if the line is not empty
e965a0
+    # If the line is not empty
e965a0
     if splitted:
e965a0
-        # if first 2 characters are '-', it is not new element, but ending of pair element
e965a0
+        # If first 2 characters are '-', it is not new element, but ending of pair element
e965a0
         if splitted[0][0] == '-' and splitted[0][1] == '-':
e965a0
             element = ""
e965a0
         else:
e965a0
@@ -113,53 +115,82 @@
e965a0
     else:
e965a0
         return 0, "", {}, ""
e965a0
 
e965a0
-    # parsing the rest of the line
e965a0
+    # Parsing the rest of the line
e965a0
     for part in splitted:
e965a0
-        # if flag is set, string is an elements content
e965a0
+        # If flag is set, string is an elements content
e965a0
         if CONTENT_FLAG == 1:
e965a0
-            # First and last characters(quotes) stripped and
e965a0
-            # string is decoded from base64
e965a0
-            content = base64.b64decode(part[1:-1])
e965a0
-            # end parsing after content is stored
e965a0
+            # String is decoded from base64
e965a0
+            try:
e965a0
+                content = base64.b64decode(part)
e965a0
+            except TypeError as e:
e965a0
+                sys.stderr.write('Failed to decode string \'%s\' from base64.\
e965a0
+                        \nError: %s\nExiting unsuccessfully.\n' % (part[1:-1], e))
e965a0
+                exit(1)
e965a0
+            # End parsing after content is stored
e965a0
             break
e965a0
-        # test if string is an elements content indicator
e965a0
+        # Test if string is an elements content indicator
e965a0
         if part == '--':
e965a0
             CONTENT_FLAG = 1
e965a0
             continue
e965a0
-        # test if string is an elements time attribute
e965a0
+
e965a0
+        # Test if string is the elements time attribute
e965a0
         if re.match(r'^--timestamp=', part):
e965a0
             attribute_name = "timestamp"
e965a0
-            # Value is string after '=' sign and without first abd last char(quotes)
e965a0
-            attribute_value = part.split('=', 1)[1][1:-1]
e965a0
-            attributes[attribute_name] = time.strftime(TIME_FORMAT, time.localtime(int(attribute_value)))
e965a0
+            # Value is string after '=' sign
e965a0
+            attribute_value = part.split('=', 1)[1]
e965a0
+            try:
e965a0
+                attributes[attribute_name] = time.strftime(TIME_FORMAT, time.localtime(int(attribute_value)))
e965a0
+            except ValueError as e:
e965a0
+                sys.stderr.write('Failed to convert timestamp attribute to int.\
e965a0
+                        \nError: %s\nExiting unsuccessfully.\n' % (e))
e965a0
+                exit(1)
e965a0
             continue
e965a0
-        # test if string is an elements regular attribute
e965a0
+
e965a0
+        # Test if string is the elements regular attribute
e965a0
         if re.match(r'^--[a-zA-Z0-9]+=', part):
e965a0
             attribute_name = part.split('=', 1)[0][2:]
e965a0
-            # Value is string after '=' sign and without first abd last char(quotes)
e965a0
-            attribute_value = part.split('=', 1)[1][1:-1]
e965a0
-            attributes[attribute_name] = base64.b64decode(attribute_value)
e965a0
+            # Value is string after '=' sign
e965a0
+            attribute_value = part.split('=', 1)[1]
e965a0
+            try:
e965a0
+                attributes[attribute_name] = base64.b64decode(attribute_value)
e965a0
+            except TypeError as e:
e965a0
+                sys.stderr.write('Failed to decode string \'%s\' from base64.\
e965a0
+                        \nError: %s\nExiting unsuccessfully.\n' % (attribute_value, e))
e965a0
+                exit(1)
e965a0
             continue
e965a0
 
e965a0
     return indent, element, attributes, content
e965a0
 
e965a0
 
e965a0
-# Returns xml element created with
e965a0
+# Returns XML element created with
e965a0
 # information given as parameters
e965a0
 def createElement(element, attributes, content):
e965a0
-    element = unicode(element, 'utf-8', errors='replace').translate(xmlTrans)
e965a0
+    # In python 3 decoding from base64 causes retyping into bytes.
e965a0
+    if isinstance(element, bytes):
e965a0
+        # First bytes are decoded from utf8.
e965a0
+        element = element.decode('utf8', 'replace')
e965a0
+    # And then retyped to string, using 'six' module which adds python 2/3 compatible methods.
e965a0
+    # XML not compatible characters are then also stripped from the string.
e965a0
+    element = six.text_type(element).translate(xmlTrans)
e965a0
+
e965a0
     try:
e965a0
         new_el = etree.Element(element)
e965a0
-    except ValueError, e:
e965a0
+    except ValueError as e:
e965a0
         sys.stderr.write('Failed to create element with name %s\nError: %s\nExiting unsuccessfully.\n' % (element, e))
e965a0
         exit(1)
e965a0
 
e965a0
-    content = unicode(content, 'utf-8', errors='replace').translate(xmlTrans)
e965a0
-    new_el.text = content
e965a0
-
e965a0
-    for key, value in attributes.iteritems():
e965a0
-        key = unicode(key, 'utf-8', errors='replace').translate(xmlTrans)
e965a0
-        value = unicode(value, 'utf-8', errors='replace').translate(xmlTrans)
e965a0
+    if isinstance(content, bytes):
e965a0
+        content = content.decode('utf8', 'replace')
e965a0
+    new_el.text = six.text_type(content).translate(xmlTrans)
e965a0
+
e965a0
+    for key, value in attributes.items():
e965a0
+        if isinstance(key, bytes):
e965a0
+            key = key.decode('utf8', 'replace')
e965a0
+        key = six.text_type(key).translate(xmlTrans)
e965a0
+
e965a0
+        if isinstance(value, bytes):
e965a0
+            value = value.decode('utf8', 'replace')
e965a0
+        value = six.text_type(value).translate(xmlTrans)
e965a0
         new_el.set(key, value)
e965a0
     return new_el
e965a0
 
e965a0
@@ -172,7 +203,7 @@
e965a0
     if options.metafile:
e965a0
         try:
e965a0
             fh = open(options.metafile, 'r+')
e965a0
-        except IOError, e:
e965a0
+        except IOError as e:
e965a0
             sys.stderr.write('Failed to open queue file with' + str(e), 'FAIL')
e965a0
             return 1
e965a0
 
e965a0
@@ -205,8 +236,8 @@
e965a0
             previous_el = new_el
e965a0
 
e965a0
         elif indent == old_indent:
e965a0
-            # Closing element with updates to it with no elements inside it
e965a0
             # TODO refactor
e965a0
+            # Closing element with updates to it with no elements inside it
e965a0
             if element == "":
e965a0
                 # Updating start and end time
e965a0
                 starttime, endtime = getStartEndTime(previous_el)
e965a0
@@ -214,9 +245,9 @@
e965a0
                 if "timestamp" in attributes:
e965a0
                     endtime = attributes["timestamp"]
e965a0
                 # Updating attributes found on closing line
e965a0
-                for key, value in attributes.iteritems():
e965a0
+                for key, value in attributes.items():
e965a0
                     previous_el.set(key, value)
e965a0
-                # add start/end time and remove timestamp attribute
e965a0
+                # Add start/end time and remove timestamp attribute
e965a0
                 addStartEndTime(previous_el, starttime, endtime)
e965a0
             # New element is on the same level as previous one
e965a0
             else:
e965a0
@@ -231,7 +262,7 @@
e965a0
         elif indent < old_indent:
e965a0
             # Difference between indent levels = how many paired elements will be closed
e965a0
             indent_diff = old_indent - indent
e965a0
-            for _ in xrange(indent_diff):
e965a0
+            for _ in range(indent_diff):
e965a0
                 el_stack.peek().append(previous_el)
e965a0
                 previous_el = el_stack.pop()
e965a0
 
e965a0
@@ -243,9 +274,9 @@
e965a0
                 if "timestamp" in attributes:
e965a0
                     endtime = attributes["timestamp"]
e965a0
                 # Updating attributes found on closing line
e965a0
-                for key, value in attributes.iteritems():
e965a0
+                for key, value in attributes.items():
e965a0
                     previous_el.set(key, value)
e965a0
-                # add start/end time and remove timestamp attribute
e965a0
+                # Add start/end time and remove timestamp attribute
e965a0
                 addStartEndTime(previous_el, starttime, endtime)
e965a0
 
e965a0
             # Ending paired element and creating new one on the same level as the paired one that just ended
e965a0
@@ -285,9 +316,9 @@
e965a0
             xslt = etree.parse(options.xslt)
e965a0
             transform = etree.XSLT(xslt)
e965a0
             journal = transform(journal)
e965a0
-    except etree.LxmlError:
e965a0
-        sys.stderr.write("\nTransformation template file " + options.xslt +
e965a0
-                         " could not be parsed.\nAborting journal creation.")
e965a0
+    except etree.LxmlError as e:
e965a0
+        sys.stderr.write("\nTransformation template file \'" + options.xslt +
e965a0
+                "\' could not be parsed.\nError: %s\nAborting journal creation.") % (e)
e965a0
         return 1
e965a0
 
e965a0
     if options.journal:
e965a0
diff -u a/src/python/rlMemAvg.py b/src/python/new/rlMemAvg.py
e965a0
--- a/src/python/rlMemAvg.py	2018-06-25 21:01:54.490910141 +0200
e965a0
+++ b/src/python/new/rlMemAvg.py	2018-06-25 21:06:24.000000000 +0200
e965a0
@@ -1,4 +1,4 @@
e965a0
-#!/usr/bin/python2
e965a0
+#!/usr/bin/python3
e965a0
 
e965a0
 # Authors:  Petr Muller     <pmuller@redhat.com>
e965a0
 #
e965a0
@@ -18,6 +18,7 @@
e965a0
 # along with this program; if not, write to the Free Software
e965a0
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
e965a0
 
e965a0
+from __future__ import print_function
e965a0
 import sys, time, re
e965a0
 
e965a0
 use_sub   = False
e965a0
@@ -31,7 +32,7 @@
e965a0
   use_popen = True
e965a0
 
e965a0
 if len(sys.argv) < 2:
e965a0
-  print 'syntax: rlMemAvg <command>'
e965a0
+  print('syntax: rlMemAvg <command>')
e965a0
   sys.exit(1)
e965a0
 
e965a0
 proglist = sys.argv[1:]
e965a0
@@ -59,4 +60,4 @@
e965a0
   if (use_sub and finish != None) or (use_popen and finish != -1):
e965a0
     break
e965a0
 
e965a0
-print "%d" % (memsum/tick)
e965a0
+print("%d" % (memsum/tick))
e965a0
diff -u a/src/python/rlMemPeak.py b/src/python/new/rlMemPeak.py
e965a0
--- a/src/python/rlMemPeak.py	2018-06-25 21:01:54.491910137 +0200
e965a0
+++ b/src/python/new/rlMemPeak.py	2018-06-25 21:06:28.000000000 +0200
e965a0
@@ -1,6 +1,6 @@
e965a0
-#!/usr/bin/python2
e965a0
+#!/usr/bin/python3
e965a0
 
e965a0
-# Authors:  Petr Muller     <pmuller@redhat.com> 
e965a0
+# Authors:  Petr Muller     <pmuller@redhat.com>
e965a0
 #
e965a0
 # Description: Prints a memory consumption peak of an executed program
e965a0
 #
e965a0
@@ -18,6 +18,7 @@
e965a0
 # along with this program; if not, write to the Free Software
e965a0
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
e965a0
 
e965a0
+from __future__ import print_function
e965a0
 import sys, time, re
e965a0
 
e965a0
 use_sub   = False
e965a0
@@ -31,7 +32,7 @@
e965a0
   use_popen = True
e965a0
 
e965a0
 if len(sys.argv) < 2:
e965a0
-  print 'syntax: rlMemPeak <command>'
e965a0
+  print('syntax: rlMemPeak <command>')
e965a0
   sys.exit(1)
e965a0
 
e965a0
 proglist = sys.argv[1:]
e965a0
@@ -57,4 +58,4 @@
e965a0
   if (use_sub and finish != None) or (use_popen and finish != -1):
e965a0
     break
e965a0
 
e965a0
-print "%d" % (maxmem)
e965a0
+print("%d" % (maxmem))
e965a0
diff -u a/src/python/testwatcher.py b/src/python/new/testwatcher.py
e965a0
--- a/src/python/testwatcher.py	2018-06-25 21:01:54.491910137 +0200
e965a0
+++ b/src/python/new/testwatcher.py	2018-06-25 21:06:32.000000000 +0200
e965a0
@@ -1,4 +1,4 @@
e965a0
-#!/usr/bin/python2 -u
e965a0
+#!/usr/bin/python3
e965a0
 #
e965a0
 # Authors:  Jiri Jaburek    <jjaburek@redhat.com>
e965a0
 #
e965a0
@@ -54,6 +54,7 @@
e965a0
 #       and the test sends the cleanup path to the watcher again
e965a0
 
e965a0
 
e965a0
+from __future__ import print_function
e965a0
 import os
e965a0
 import sys
e965a0
 import signal
e965a0
@@ -105,12 +106,12 @@
e965a0
 ### HELPERS
e965a0
 #
e965a0
 def debug(msg):
e965a0
-    print 'TESTWATCHER: '+msg
e965a0
+    print('TESTWATCHER: '+msg)
e965a0
     sys.stdout.flush()
e965a0
 
e965a0
 
e965a0
 def fatal(msg):
e965a0
-    print >> sys.stderr, 'TESTWATCHER fatal: '+msg
e965a0
+    print('TESTWATCHER fatal: '+msg, file=sys.stderr)
e965a0
     sys.stderr.flush()
e965a0
     sys.exit(1)
e965a0
 
e965a0
@@ -153,13 +154,13 @@
e965a0
     debug('hooking beah LWD')
e965a0
     try:
e965a0
         os.makedirs(os.path.dirname(lwd_guard_file))
e965a0
-    except OSError, e:
e965a0
+    except OSError as e:
e965a0
         if e.errno == errno.EEXIST:
e965a0
             pass
e965a0
     f = open(lwd_guard_file, 'w')
e965a0
     f.write(watchdog_guard_cont)
e965a0
     f.close()
e965a0
-    os.chmod(lwd_guard_file, 0755)
e965a0
+    os.chmod(lwd_guard_file, 0o755)
e965a0
 
e965a0
 
e965a0
 # called when EWD (external watchdog) is about to expire
e965a0
@@ -234,7 +235,7 @@
e965a0
             try:
e965a0
                 os.waitpid(cleanuppid, 0)
e965a0
                 cleanuppid = 0
e965a0
-            except OSError, e:
e965a0
+            except OSError as e:
e965a0
                 if e.errno == errno.EINTR:
e965a0
                     pass
e965a0
                 if e.errno == errno.ECHILD:
e965a0
@@ -291,7 +292,7 @@
e965a0
                 # wait for entire process group
e965a0
                 os.waitpid(testpid, 0)
e965a0
                 testpid = 0
e965a0
-            except OSError, e:
e965a0
+            except OSError as e:
e965a0
                 # no traceback if interrupted by a signal
e965a0
                 if e.errno == errno.EINTR:
e965a0
                     pass