krb5 commit: Convert Python tests to Python 3

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/e23d24beacb73581bbf4351250f3955e6fd44361
commit e23d24beacb73581bbf4351250f3955e6fd44361
Author: Greg Hudson <[email protected]>
Date:   Mon Feb 19 21:10:09 2018 -0500

    Convert Python tests to Python 3
    
    Look for python3 in configure.in and verify that we got it.  Convert
    test code to conform to Python 3.
    
    ticket: 8710 (new)

 src/Makefile.in                  |    1 +
 src/configure.in                 |    6 +---
 src/kadmin/dbutil/t_tdumputil.py |    4 +-
 src/tests/jsonwalker.py          |   16 ++++++------
 src/tests/t_cve-2012-1014.py     |    2 +-
 src/tests/t_cve-2012-1015.py     |    2 +-
 src/tests/t_hostrealm.py         |    4 ++-
 src/tests/t_kdb.py               |   11 +++----
 src/tests/t_keytab.py            |   34 ++++++++++++------------
 src/tests/t_mkey.py              |    6 ++--
 src/tests/t_otp.py               |    7 +++--
 src/tests/t_tabdump.py           |    4 +-
 src/util/Makefile.in             |    1 +
 src/util/k5test.py               |   51 ++++++++++++++++++++------------------
 src/util/princflags.py           |   25 +++++++++---------
 15 files changed, 89 insertions(+), 85 deletions(-)

diff --git a/src/Makefile.in b/src/Makefile.in
index 40a2e56..b57d058 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -532,6 +532,7 @@ runenv.py: pyrunenv.vals
 
 clean-unix::
 	$(RM) runenv.py runenv.pyc pyrunenv.vals
+	$(RM) -r __pycache__
 
 COV_BUILD=	cov-build
 COV_ANALYZE=	cov-analyze
diff --git a/src/configure.in b/src/configure.in
index c493257..bafacf8 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -1100,15 +1100,13 @@ fi
 AC_SUBST(HAVE_RUNTEST)
 
 # For Python tests.
-AC_CHECK_PROG(PYTHON,python2,python2)
+AC_CHECK_PROG(PYTHON,python3,python3)
 if text x"$PYTHON" = x; then
 	AC_CHECK_PROG(PYTHON,python,python)
 fi
 HAVE_PYTHON=no
 if test x"$PYTHON" != x; then
-	# k5test.py requires python 2.4 (for the subprocess module).
-	# Some code needs python 2.5 (for syntax like conditional expressions).
-	wantver="(sys.hexversion >= 0x2050000 and sys.hexversion < 0x3000000)"
+	wantver="(sys.hexversion >= 0x3000000)"
 	if "$PYTHON" -c "import sys; sys.exit(not $wantver and 1 or 0)"; then
 		HAVE_PYTHON=yes
 	fi
diff --git a/src/kadmin/dbutil/t_tdumputil.py b/src/kadmin/dbutil/t_tdumputil.py
index 52e3565..47b2aa7 100755
--- a/src/kadmin/dbutil/t_tdumputil.py
+++ b/src/kadmin/dbutil/t_tdumputil.py
@@ -6,8 +6,8 @@ realm = K5Realm(create_kdb=False)
 def compare(s, expected, msg):
     if s == expected:
         return
-    print 'expected:', repr(expected)
-    print 'got:', repr(s)
+    print('expected:', repr(expected))
+    print('got:', repr(s))
     fail(msg)
 
 out = realm.run(['./t_tdumputil', '2', 'field1', 'field2',
diff --git a/src/tests/jsonwalker.py b/src/tests/jsonwalker.py
index 942ca2d..7a0675e 100644
--- a/src/tests/jsonwalker.py
+++ b/src/tests/jsonwalker.py
@@ -2,8 +2,8 @@ import sys
 try:
     import cjson
 except ImportError:
-    print "Warning: skipping audit log verification because the cjson module" \
-          " is unavailable"
+    print("Warning: skipping audit log verification because the cjson module" \
+          " is unavailable")
     sys.exit(0)
 from collections import defaultdict
 from optparse import OptionParser
@@ -22,10 +22,10 @@ class Parser(object):
         result = self.parse(logs)
         if len(result) != len(self.defaults):
             diff = set(self.defaults.keys()).difference(result.keys())
-            print 'Test failed.'
-            print 'The following attributes were not set:'
+            print('Test failed.')
+            print('The following attributes were not set:')
             for it in diff:
-                print it
+                print(it)
             sys.exit(1)
 
     def flatten(self, defaults):
@@ -42,7 +42,7 @@ class Parser(object):
         result = dict()
         for path,value in self._walk(defaults):
             if path in result:
-                print 'Warning: attribute path %s already exists' % path
+                print('Warning: attribute path %s already exists' % path)
             result[path] = value
 
         return result
@@ -60,7 +60,7 @@ class Parser(object):
                         if v is not None:
                             dv = self.DEFAULTS[type(v)]
                         else:
-                            print 'Warning: attribute %s is set to None' % a
+                            print('Warning: attribute %s is set to None' % a)
                             continue
                     # by now we have default value
                     if v != dv:
@@ -96,7 +96,7 @@ if __name__ == '__main__':
                 content.append(cjson.decode(l.rstrip()))
         f.close()
     else:
-        print 'Input file in jason format is required'
+        print('Input file in jason format is required')
         exit()
 
     defaults = None
diff --git a/src/tests/t_cve-2012-1014.py b/src/tests/t_cve-2012-1014.py
index dcff95f..8447e0e 100755
--- a/src/tests/t_cve-2012-1014.py
+++ b/src/tests/t_cve-2012-1014.py
@@ -20,7 +20,7 @@ x2 = base64.b16decode('A44F304DA007030500FEDCBA90A10E30' +
                       '01')
 
 for x in range(11, 128):
-    s.sendto(''.join([x1, chr(x), x2]), a)
+    s.sendto(x1 + bytes([x]) + x2, a)
 
 # Make sure kinit still works.
 
diff --git a/src/tests/t_cve-2012-1015.py b/src/tests/t_cve-2012-1015.py
index 28b1e61..ae5678c 100755
--- a/src/tests/t_cve-2012-1015.py
+++ b/src/tests/t_cve-2012-1015.py
@@ -27,7 +27,7 @@ x1 = base64.b16decode('6A81A030819DA103020105A20302010A' +
 x2 = base64.b16decode('A8083006020106020112')
 
 for x in range(0, 128):
-    s.sendto(''.join([x1, chr(x), x2]), a)
+    s.sendto(x1 + bytes([x]) + x2, a)
 
 # Make sure kinit still works.
 
diff --git a/src/tests/t_hostrealm.py b/src/tests/t_hostrealm.py
index 18319bd..7cec3b8 100755
--- a/src/tests/t_hostrealm.py
+++ b/src/tests/t_hostrealm.py
@@ -131,7 +131,9 @@ testd(realm, 'KRBTEST.COM', 'default_realm profile', env=notest2)
 # this behavior.
 mark('default_realm: test1 module')
 remove_default = {'libdefaults': {'default_realm': None}}
-nodefault_conf = dict(disable_conf.items() + remove_default.items())
+# Python 3.5+: nodefault_conf = {**disable_conf, **remove_default}
+nodefault_conf = dict(list(disable_conf.items()) +
+                      list(remove_default.items()))
 nodefault = realm.special_env('nodefault', False, krb5_conf=nodefault_conf)
 testd(realm, 'one', 'default_realm test1', env=nodefault)
 
diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py
index fc0df92..7a082a5 100755
--- a/src/tests/t_kdb.py
+++ b/src/tests/t_kdb.py
@@ -1,6 +1,5 @@
 from k5test import *
 import time
-from itertools import imap
 
 # Run kdbtest against the non-LDAP KDB modules.
 for realm in multidb_realms(create_kdb=False):
@@ -51,7 +50,7 @@ else:
 def slap_add(ldif):
     proc = subprocess.Popen([slapadd, '-b', 'cn=config', '-F', slapd_conf],
                             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                            stderr=subprocess.STDOUT)
+                            stderr=subprocess.STDOUT, universal_newlines=True)
     (out, dummy) = proc.communicate(ldif)
     output(out)
     return proc.wait()
@@ -98,7 +97,7 @@ if slap_add('include: file://%s\n' % schema) != 0:
 ldap_homes = ['/etc/ldap', '/etc/openldap', '/usr/local/etc/openldap',
               '/usr/local/etc/ldap']
 local_schema_path = '/schema/core.ldif'
-core_schema = next((i for i in imap(lambda x:x+local_schema_path, ldap_homes)
+core_schema = next((i for i in map(lambda x:x+local_schema_path, ldap_homes)
                     if os.path.isfile(i)), None)
 if core_schema:
     if slap_add('include: file://%s\n' % core_schema) != 0:
@@ -114,7 +113,7 @@ atexit.register(kill_slapd)
 
 out = open(slapd_out, 'w')
 subprocess.call([slapd, '-h', ldap_uri, '-F', slapd_conf], stdout=out,
-                stderr=out)
+                stderr=out, universal_newlines=True)
 out.close()
 pidf = open(slapd_pidfile, 'r')
 slapd_pid = int(pidf.read())
@@ -158,7 +157,7 @@ def ldap_search(args):
     proc = subprocess.Popen([ldapsearch, '-H', ldap_uri, '-b', top_dn,
                              '-D', admin_dn, '-w', admin_pw, args],
                             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                            stderr=subprocess.STDOUT)
+                            stderr=subprocess.STDOUT, universal_newlines=True)
     (out, dummy) = proc.communicate()
     return out
 
@@ -166,7 +165,7 @@ def ldap_modify(ldif, args=[]):
     proc = subprocess.Popen([ldapmodify, '-H', ldap_uri, '-D', admin_dn,
                              '-x', '-w', admin_pw] + args,
                             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                            stderr=subprocess.STDOUT)
+                            stderr=subprocess.STDOUT, universal_newlines=True)
     (out, dummy) = proc.communicate(ldif)
     output(out)
 
diff --git a/src/tests/t_keytab.py b/src/tests/t_keytab.py
index 0bc4e6e..950517d 100755
--- a/src/tests/t_keytab.py
+++ b/src/tests/t_keytab.py
@@ -99,36 +99,36 @@ mark('32-bit kvno')
 
 # Test that klist -k can read a keytab entry without a 32-bit kvno and
 # reports the 8-bit key version.
-record = '\x00\x01'             # principal component count
-record += '\x00\x0bKRBTEST.COM' # realm
-record += '\x00\x04user'        # principal component
-record += '\x00\x00\x00\x01'    # name type (NT-PRINCIPAL)
-record += '\x54\xf7\x4d\x35'    # timestamp
-record += '\x02'                # key version
-record += '\x00\x12'            # enctype
-record += '\x00\x20'            # key length
-record += '\x00' * 32           # key bytes
-f = open(realm.keytab, 'w')
-f.write('\x05\x02\x00\x00\x00' + chr(len(record)))
+record = b'\x00\x01'             # principal component count
+record += b'\x00\x0bKRBTEST.COM' # realm
+record += b'\x00\x04user'        # principal component
+record += b'\x00\x00\x00\x01'    # name type (NT-PRINCIPAL)
+record += b'\x54\xf7\x4d\x35'    # timestamp
+record += b'\x02'                # key version
+record += b'\x00\x12'            # enctype
+record += b'\x00\x20'            # key length
+record += b'\x00' * 32           # key bytes
+f = open(realm.keytab, 'wb')
+f.write(b'\x05\x02\x00\x00\x00' + bytes([len(record)]))
 f.write(record)
 f.close()
 msg = '   2 %s' % realm.user_princ
 out = realm.run([klist, '-k'], expected_msg=msg)
 
 # Make sure zero-fill isn't treated as a 32-bit kvno.
-f = open(realm.keytab, 'w')
-f.write('\x05\x02\x00\x00\x00' + chr(len(record) + 4))
+f = open(realm.keytab, 'wb')
+f.write(b'\x05\x02\x00\x00\x00' + bytes([len(record) + 4]))
 f.write(record)
-f.write('\x00\x00\x00\x00')
+f.write(b'\x00\x00\x00\x00')
 f.close()
 msg = '   2 %s' % realm.user_princ
 out = realm.run([klist, '-k'], expected_msg=msg)
 
 # Make sure a hand-crafted 32-bit kvno is recognized.
-f = open(realm.keytab, 'w')
-f.write('\x05\x02\x00\x00\x00' + chr(len(record) + 4))
+f = open(realm.keytab, 'wb')
+f.write(b'\x05\x02\x00\x00\x00' + bytes([len(record) + 4]))
 f.write(record)
-f.write('\x00\x00\x00\x03')
+f.write(b'\x00\x00\x00\x03')
 f.close()
 msg = '   3 %s' % realm.user_princ
 out = realm.run([klist, '-k'], expected_msg=msg)
diff --git a/src/tests/t_mkey.py b/src/tests/t_mkey.py
index c52fba6..99273c9 100755
--- a/src/tests/t_mkey.py
+++ b/src/tests/t_mkey.py
@@ -310,10 +310,10 @@ realm.stop()
 mark('pre-1.7 stash file')
 dumpfile = os.path.join(srctop, 'tests', 'dumpfiles', 'dump.16')
 os.remove(stash_file)
-f = open(stash_file, 'w')
+f = open(stash_file, 'wb')
 f.write(struct.pack('=HL24s', 16, 24,
-                    '\xF8\x3E\xFB\xBA\x6D\x80\xD9\x54\xE5\x5D\xF2\xE0'
-                    '\x94\xAD\x6D\x86\xB5\x16\x37\xEC\x7C\x8A\xBC\x86'))
+                    b'\xF8\x3E\xFB\xBA\x6D\x80\xD9\x54\xE5\x5D\xF2\xE0'
+                    b'\x94\xAD\x6D\x86\xB5\x16\x37\xEC\x7C\x8A\xBC\x86'))
 f.close()
 realm.run([kdb5_util, 'load', dumpfile])
 nprincs = len(realm.run([kadminl, 'listprincs']).splitlines())
diff --git a/src/tests/t_otp.py b/src/tests/t_otp.py
index c7e7363..0160fcd 100755
--- a/src/tests/t_otp.py
+++ b/src/tests/t_otp.py
@@ -29,8 +29,8 @@
 #
 
 from k5test import *
-from Queue import Empty
-import StringIO
+from queue import Empty
+from io import StringIO
 import struct
 
 try:
@@ -120,7 +120,8 @@ class UnixRadiusDaemon(RadiusDaemon):
         sock.listen(1)
         return (sock, addr)
 
-    def recvRequest(self, (sock, addr)):
+    def recvRequest(self, sock_and_addr):
+        sock, addr = sock_and_addr
         conn = sock.accept()[0]
         sock.close()
         os.remove(addr)
diff --git a/src/tests/t_tabdump.py b/src/tests/t_tabdump.py
index 2a86136..49531bf 100755
--- a/src/tests/t_tabdump.py
+++ b/src/tests/t_tabdump.py
@@ -1,10 +1,10 @@
 from k5test import *
 
 import csv
-import StringIO
+from io import StringIO
 
 def tab_csv(s):
-    io = StringIO.StringIO(s)
+    io = StringIO(s)
     return list(csv.DictReader(io, dialect=csv.excel_tab))
 
 
diff --git a/src/util/Makefile.in b/src/util/Makefile.in
index 764c1ab..b80ffbe 100644
--- a/src/util/Makefile.in
+++ b/src/util/Makefile.in
@@ -26,3 +26,4 @@ install:
 
 clean-unix::
 	$(RM) *.pyc
+	$(RM) -r __pycache__
diff --git a/src/util/k5test.py b/src/util/k5test.py
index 8d635af..848eb02 100644
--- a/src/util/k5test.py
+++ b/src/util/k5test.py
@@ -389,18 +389,18 @@ import imp
 def fail(msg):
     """Print a message and exit with failure."""
     global _current_pass
-    print "*** Failure:", msg
+    print("*** Failure:", msg)
     if _last_mark:
-        print "*** Last mark: %s" % _last_mark
+        print("*** Last mark: %s" % _last_mark)
     if _last_cmd:
-        print "*** Last command (#%d): %s" % (_cmd_index - 1, _last_cmd)
+        print("*** Last command (#%d): %s" % (_cmd_index - 1, _last_cmd))
     if _last_cmd_output:
-        print "*** Output of last command:"
+        print("*** Output of last command:")
         sys.stdout.write(_last_cmd_output)
     if _current_pass:
-        print "*** Failed in test pass:", _current_pass
+        print("*** Failed in test pass:", _current_pass)
     if _current_db:
-        print "*** Failed with db:", _current_db
+        print("*** Failed with db:", _current_db)
     sys.exit(1)
 
 
@@ -476,15 +476,16 @@ def _onexit():
         if not verbose:
             testlogfile = os.path.join(os.getcwd(), 'testlog')
             utildir = os.path.join(srctop, 'util')
-            print 'For details, see: %s' % testlogfile
-            print 'Or re-run this test script with the -v flag:'
-            print '    cd %s' % os.getcwd()
-            print '    PYTHONPATH=%s %s %s -v' % \
-                (utildir, sys.executable, sys.argv[0])
-            print
-        print 'Use --debug=NUM to run a command under a debugger.  Use'
-        print '--stop-after=NUM to stop after a daemon is started in order to'
-        print 'attach to it with a debugger.  Use --help to see other options.'
+            print('For details, see: %s' % testlogfile)
+            print('Or re-run this test script with the -v flag:')
+            print('    cd %s' % os.getcwd())
+            print('    PYTHONPATH=%s %s %s -v' %
+                  (utildir, sys.executable, sys.argv[0]))
+            print()
+        print('Use --debug=NUM to run a command under a debugger.  Use')
+        print('--stop-after=NUM to stop after a daemon is started in order to')
+        print('attach to it with a debugger.  Use --help to see other')
+        print('options.')
 
 
 def _onsigint(signum, frame):
@@ -534,8 +535,8 @@ def _get_hostname():
     hostname = socket.gethostname()
     try:
         ai = socket.getaddrinfo(hostname, None, 0, 0, 0, socket.AI_CANONNAME)
-    except socket.gaierror, (error, errstr):
-        fail('Local hostname "%s" does not resolve: %s.' % (hostname, errstr))
+    except socket.gaierror as e:
+        fail('Local hostname "%s" does not resolve: %s.' % (hostname, e[1]))
     (family, socktype, proto, canonname, sockaddr) = ai[0]
     try:
         name = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD)
@@ -605,7 +606,7 @@ def _match_cmdnum(cmdnum, ind):
 def _build_env():
     global buildtop, runenv
     env = os.environ.copy()
-    for (k, v) in runenv.env.iteritems():
+    for (k, v) in runenv.env.items():
         if v.find('./') == 0:
             env[k] = os.path.join(buildtop, v)
         else:
@@ -715,7 +716,8 @@ def _run_cmd(args, env, input=None, expected_code=0, expected_msg=None,
 
     # Run the command and log the result, folding stderr into stdout.
     proc = subprocess.Popen(args, stdin=infile, stdout=subprocess.PIPE,
-                            stderr=subprocess.STDOUT, env=env)
+                            stderr=subprocess.STDOUT, env=env,
+                            universal_newlines=True)
     (outdata, dummy_errdata) = proc.communicate(input)
     _last_cmd_output = outdata
     code = proc.returncode
@@ -745,10 +747,10 @@ def _debug_cmd(args, env, input):
            (_cmd_index, _shell_equiv(args)), True)
     if input:
         print
-        print '*** Enter the following input when appropriate:'
-        print 
-        print input
-        print
+        print('*** Enter the following input when appropriate:')
+        print()
+        print(input)
+        print()
     code = subprocess.call(args, env=env)
     output('*** [%d] Completed in debugger with return code %d\n' %
            (_cmd_index, code))
@@ -776,7 +778,8 @@ def _start_daemon(args, env, sentinel):
 
     # Start the daemon and look for the sentinel in stdout or stderr.
     proc = subprocess.Popen(args, stdin=null_input, stdout=subprocess.PIPE,
-                            stderr=subprocess.STDOUT, env=env)
+                            stderr=subprocess.STDOUT, env=env,
+                            universal_newlines=True)
     _last_cmd_output = ''
     while True:
         line = proc.stdout.readline()
diff --git a/src/util/princflags.py b/src/util/princflags.py
index f568dd2..f645e86 100644
--- a/src/util/princflags.py
+++ b/src/util/princflags.py
@@ -1,5 +1,4 @@
 import re
-import string
 
 # Module for translating KDB principal flags between string and
 # integer forms.
@@ -81,7 +80,7 @@ _prefixlen = len(_prefix)
 _flagnames = {}
 
 # Translation table to map hyphens to underscores
-_squash = string.maketrans('-', '_')
+_squash = str.maketrans('-', '_')
 
 # Combined input-to-flag lookup table, to be filled in by
 # _setup_tables()
@@ -176,7 +175,7 @@ def flagnum2str(n):
 # Return a list of flag names from a flag word.
 def flags2namelist(flags):
     a = []
-    for n in xrange(32):
+    for n in range(32):
         if flags & (1 << n):
             a.append(flagnum2str(n))
     return a
@@ -225,21 +224,21 @@ def speclist2mask(s):
 
 # Print C table of input flag specifiers for lib/kadm5/str_conv.c.
 def _print_ftbl():
-    print 'static const struct flag_table_row ftbl[] = {'
-    a = sorted(pflags.items(), key=lambda (k, v): (v.flag, -v.invert, k))
+    print('static const struct flag_table_row ftbl[] = {')
+    a = sorted(pflags.items(), key=lambda k, v: (v.flag, -v.invert, k))
     for k, v in a:
         s1 = '    {"%s",' % k
         s2 = '%-31s KRB5_KDB_%s,' % (s1, v.flagname())
-        print '%-63s %d},' % (s2, 1 if v.invert else 0)
+        print('%-63s %d},' % (s2, 1 if v.invert else 0))
 
-    print '};'
-    print '#define NFTBL (sizeof(ftbl) / sizeof(ftbl[0]))'
+    print('};')
+    print('#define NFTBL (sizeof(ftbl) / sizeof(ftbl[0]))')
 
 
 # Print C table of output flag names for lib/kadm5/str_conv.c.
 def _print_outflags():
-    print 'static const char *outflags[] = {'
-    for i in xrange(32):
+    print('static const char *outflags[] = {')
+    for i in range(32):
         flag = 1 << i
         if flag > max(_flagnames.keys()):
             break
@@ -247,10 +246,10 @@ def _print_outflags():
             s = '    "%s",' % _flagnames[flag]
         except KeyError:
             s = '    NULL,'
-        print '%-32s/* 0x%08x */' % (s, flag)
+        print('%-32s/* 0x%08x */' % (s, flag))
 
-    print '};'
-    print '#define NOUTFLAGS (sizeof(outflags) / sizeof(outflags[0]))'
+    print('};')
+    print('#define NOUTFLAGS (sizeof(outflags) / sizeof(outflags[0]))')
 
 
 # Print out C tables to insert into lib/kadm5/str_conv.c.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.