Blob Blame History Raw
From a9047a722ba5de38e7c1d762ffcfb74c36725fe2 Mon Sep 17 00:00:00 2001
From: Andrew Cooper <andrew.cooper3@citrix.com>
Date: Mon, 11 Mar 2019 19:18:40 +0000
Subject: [PATCH] tools/xen-foreign: Update python scripts to be Py3 compatible

The issues are:
 * dict.has_key() was completely removed in Py3
 * dict.keys() is an iterable rather than list in Py3, so .sort() doesn't work.
 * list.sort(cmp=) was deprecated in Py2.4 and removed in Py3.

The has_key() issue is trivially fixed by switching to using the in keyword.
The sorting issue could be trivially fixed, but take the opportunity to
improve the code.

The reason for the sorting is to ensure that "unsigned long" gets replaced
before "long", and the only reason sorting is necessary is because
inttypes[arch] is needlessly a dictionary.  Update inttypes[arch] to be a list
of tuples rather than a dictionary, and process them in list order.

Reported-by: George Dunlap <george.dunlap@eu.citrix.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Wei Liu <wei.liu2@citrix.com>
---
 tools/include/xen-foreign/mkchecker.py |  2 +-
 tools/include/xen-foreign/mkheader.py  | 58 +++++++++++++-------------
 2 files changed, 29 insertions(+), 31 deletions(-)

diff --git a/tools/include/xen-foreign/mkchecker.py b/tools/include/xen-foreign/mkchecker.py
index fdad869a91..199b0eebbc 100644
--- a/tools/include/xen-foreign/mkchecker.py
+++ b/tools/include/xen-foreign/mkchecker.py
@@ -37,7 +37,7 @@ for struct in structs:
     f.write('\tprintf("%%-25s |", "%s");\n' % struct);
     for a in archs:
         s = struct + "_" + a;
-        if compat_arches.has_key(a):
+        if a in compat_arches:
             compat = compat_arches[a]
             c = struct + "_" + compat;
         else:
diff --git a/tools/include/xen-foreign/mkheader.py b/tools/include/xen-foreign/mkheader.py
index 97e0c7a984..fb268f0dce 100644
--- a/tools/include/xen-foreign/mkheader.py
+++ b/tools/include/xen-foreign/mkheader.py
@@ -17,13 +17,13 @@ header = {};
 footer = {};
 
 #arm
-inttypes["arm32"] = {
-    "unsigned long" : "__danger_unsigned_long_on_arm32",
-    "long"          : "__danger_long_on_arm32",
-    "xen_pfn_t"     : "uint64_t",
-    "xen_ulong_t"   : "uint64_t",
-    "uint64_t"      : "__align8__ uint64_t",
-};
+inttypes["arm32"] = [
+    ("unsigned long", "__danger_unsigned_long_on_arm32"),
+    ("long",          "__danger_long_on_arm32"),
+    ("xen_pfn_t",     "uint64_t"),
+    ("xen_ulong_t",   "uint64_t"),
+    ("uint64_t",      "__align8__ uint64_t"),
+]
 header["arm32"] = """
 #define __arm___ARM32 1
 #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
@@ -38,13 +38,13 @@ footer["arm32"] = """
 #undef __DECL_REG
 """
 
-inttypes["arm64"] = {
-    "unsigned long" : "__danger_unsigned_long_on_arm64",
-    "long"          : "__danger_long_on_arm64",
-    "xen_pfn_t"     : "uint64_t",
-    "xen_ulong_t"   : "uint64_t",
-    "uint64_t"      : "__align8__ uint64_t",
-};
+inttypes["arm64"] = [
+    ("unsigned long", "__danger_unsigned_long_on_arm64"),
+    ("long",          "__danger_long_on_arm64"),
+    ("xen_pfn_t",     "uint64_t"),
+    ("xen_ulong_t",   "uint64_t"),
+    ("uint64_t",      "__align8__ uint64_t"),
+]
 header["arm64"] = """
 #define __aarch64___ARM64 1
 #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
@@ -60,12 +60,12 @@ footer["arm64"] = """
 """
 
 # x86_32
-inttypes["x86_32"] = {
-    "unsigned long" : "uint32_t",
-    "long"          : "uint32_t",
-    "xen_pfn_t"     : "uint32_t",
-    "xen_ulong_t"   : "uint32_t",
-};
+inttypes["x86_32"] = [
+    ("unsigned long", "uint32_t"),
+    ("long",          "uint32_t"),
+    ("xen_pfn_t",     "uint32_t"),
+    ("xen_ulong_t",   "uint32_t"),
+]
 header["x86_32"] = """
 #define __DECL_REG_LO8(which) uint32_t e ## which ## x
 #define __DECL_REG_LO16(name) uint32_t e ## name
@@ -79,12 +79,12 @@ footer["x86_32"] = """
 """;
 
 # x86_64
-inttypes["x86_64"] = {
-    "unsigned long" : "__align8__ uint64_t",
-    "long"          : "__align8__ uint64_t",
-    "xen_pfn_t"     : "__align8__ uint64_t",
-    "xen_ulong_t"   : "__align8__ uint64_t",
-};
+inttypes["x86_64"] = [
+    ("unsigned long", "__align8__ uint64_t"),
+    ("long",          "__align8__ uint64_t"),
+    ("xen_pfn_t",     "__align8__ uint64_t"),
+    ("xen_ulong_t",   "__align8__ uint64_t"),
+]
 header["x86_64"] = """
 #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
 # define __DECL_REG(name) union { uint64_t r ## name, e ## name; }
@@ -205,10 +205,8 @@ for struct in structs:
     output = re.sub("\\b(%s)_t\\b" % struct, "\\1_%s_t" % arch, output);
 
 # replace: integer types
-integers = inttypes[arch].keys();
-integers.sort(lambda a, b: cmp(len(b),len(a)));
-for type in integers:
-    output = re.sub("\\b%s\\b" % type, inttypes[arch][type], output);
+for old, new in inttypes[arch]:
+    output = re.sub("\\b%s\\b" % old, new, output)
 
 # print results
 f = open(outfile, "w");
-- 
2.17.2

From 72288c156662e764cc47605f51842b4d0fcc3582 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Mon, 11 Mar 2019 17:16:45 +0000
Subject: [PATCH 1/8] Update Python requirement to 2.6

CentOS 5, which was the reason for the 2.4 restriction, is EOL. CentOS
6 ships 2.6.

Bump the version to 2.6 in README. Update configure.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 README             | 4 ++--
 tools/configure    | 8 ++++----
 tools/configure.ac | 2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/README b/README
index 6346825bc0..d00715c2fd 100644
--- a/README
+++ b/README
@@ -46,7 +46,7 @@ provided by your OS distributor:
         - GCC 4.8 or later
         - GNU Binutils 2.24 or later
     * Development install of zlib (e.g., zlib-dev)
-    * Development install of Python 2, v2.4 or later (e.g., python-dev)
+    * Development install of Python 2, v2.6 or later (e.g., python-dev)
     * Development install of curses (e.g., libncurses-dev)
     * Development install of openssl (e.g., openssl-dev)
     * Development install of x11 (e.g. xorg-x11-dev)
@@ -177,7 +177,7 @@ Python Runtime Libraries
 
 Various tools, such as pygrub, have the following runtime dependencies:
 
-    * Python 2, v2.4 or later.
+    * Python 2, v2.6 or later.
           URL:    http://www.python.org/
           Debian: python
 
diff --git a/tools/configure b/tools/configure
index acc857510e..d0065b811d 100755
--- a/tools/configure
+++ b/tools/configure
@@ -7002,15 +7002,15 @@ if test x"${PYTHONPATH}" = x"no"
 then
     as_fn_error $? "Unable to find $PYTHON, please install $PYTHON" "$LINENO" 5
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python version >= 2.3 " >&5
-$as_echo_n "checking for python version >= 2.3 ... " >&6; }
-`$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (2, 3)"))'`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python version >= 2.6 " >&5
+$as_echo_n "checking for python version >= 2.6 ... " >&6; }
+`$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (2, 6)"))'`
 if test "$?" != "0"
 then
     python_version=`$PYTHON -V 2>&1`
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-    as_fn_error $? "$python_version is too old, minimum required version is 2.3" "$LINENO" 5
+    as_fn_error $? "$python_version is too old, minimum required version is 2.6" "$LINENO" 5
 else
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
diff --git a/tools/configure.ac b/tools/configure.ac
index 1499344ce6..c9fd69ddfa 100644
--- a/tools/configure.ac
+++ b/tools/configure.ac
@@ -358,7 +358,7 @@ AS_IF([echo "$PYTHON" | grep -q "^/"], [
 ],[test -z "$PYTHON"], [PYTHON="python"],
 [AC_MSG_ERROR([PYTHON specified, but is not an absolute path])])
 AX_PATH_PROG_OR_FAIL([PYTHONPATH], [$PYTHON])
-AX_CHECK_PYTHON_VERSION([2], [3])
+AX_CHECK_PYTHON_VERSION([2], [6])
 
 AS_IF([test "$cross_compiling" != yes], [
     AX_CHECK_PYTHON_DEVEL()
-- 
2.17.2


From e81209fd4ea705f4de4b61d05e6ab0aea9bb0b88 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Mon, 11 Mar 2019 12:58:05 +0000
Subject: [PATCH 2/8] pygrub/fsimage: drop unused struct

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 tools/pygrub/src/fsimage/fsimage.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c
index 743a3fb7b8..780207791c 100644
--- a/tools/pygrub/src/fsimage/fsimage.c
+++ b/tools/pygrub/src/fsimage/fsimage.c
@@ -43,13 +43,6 @@ typedef struct fsimage_file {
 	fsi_file_t *file;
 } fsimage_file_t;
 
-struct foo {
-	int ref;
-	int size;
-	long hash;
-	int state;
-};
-
 static PyObject *
 fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs)
 {
-- 
2.17.2


From b9e1368af14ded6aee3bdf64e8329628b16291ff Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Mon, 11 Mar 2019 12:55:29 +0000
Subject: [PATCH 3/8] pygrub: change tabs into spaces

Not sure why Python 2 never complained, but Python 3 does.

Change tabs to spaces.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 tools/pygrub/src/pygrub | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub
index 52a8965ad9..1189b1ca48 100755
--- a/tools/pygrub/src/pygrub
+++ b/tools/pygrub/src/pygrub
@@ -858,7 +858,7 @@ if __name__ == "__main__":
             output_directory = a
 
     if debug:
-	logging.basicConfig(level=logging.DEBUG)
+        logging.basicConfig(level=logging.DEBUG)
 
 
     try:
@@ -917,7 +917,7 @@ if __name__ == "__main__":
             # IOErrors raised by fsimage.open
             # RuntimeErrors raised by run_grub if no menu.lst present
             if debug:
-		traceback.print_exc()
+                traceback.print_exc()
             fs = None
             continue
 
-- 
2.17.2


From 16cc3362aed39e3093419b9df6ec73269071d063 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Tue, 5 Mar 2019 12:32:06 +0000
Subject: [PATCH 4/8] build/m4: make python_devel.m4 work with both python 2
 and 3

Do the following:

1. Change the form of "print".
2. Use AC_CHECK_FUNC to avoid the need to generate library name.
3. Remove unused stuff.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
---
 m4/python_devel.m4 | 23 ++++++-----------
 tools/configure    | 64 ++++++----------------------------------------
 2 files changed, 16 insertions(+), 71 deletions(-)

diff --git a/m4/python_devel.m4 b/m4/python_devel.m4
index 05ea4ef7e2..f9cb23aee1 100644
--- a/m4/python_devel.m4
+++ b/m4/python_devel.m4
@@ -1,38 +1,31 @@
 AC_DEFUN([AX_CHECK_PYTHON_DEVEL], [
 ac_previous_cppflags=$CPPFLAGS
 ac_previous_ldflags=$LDFLAGS
-ac_python_version=`$PYTHON -c 'import distutils.sysconfig; \
-    print distutils.sysconfig.get_config_var("VERSION")'`
 AC_PATH_PROG([pyconfig], [$PYTHON-config], [no])
 AS_IF([test x"$pyconfig" = x"no"], [
     dnl For those that don't have python-config
     CPPFLAGS="$CFLAGS `$PYTHON -c 'import distutils.sysconfig; \
         print "-I" + distutils.sysconfig.get_config_var("INCLUDEPY")'`"
     CPPFLAGS="$CPPFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("CFLAGS")'`"
-    PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("LIBS")'`"
-    PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("SYSLIBS")'`"
+        print(distutils.sysconfig.get_config_var("CFLAGS"))'`"
     LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print "-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\
-        standard_lib=1) + "/config"'`"
+        print("-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\
+        standard_lib=1) + "/config")'`"
     LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("LINKFORSHARED")'`"
+        print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`"
     LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("LDFLAGS")'`"
+        print(distutils.sysconfig.get_config_var("LDFLAGS"))'`"
 ], [
     dnl If python-config is found use it
     CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`"
     LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`"
-    PYTHON_LIBS="$LIBS `$PYTHON-config --libs`"
 ])
 
 AC_CHECK_HEADER([Python.h], [],
     [AC_MSG_ERROR([Unable to find Python development headers])],)
-AC_CHECK_LIB(python$ac_python_version, PyArg_ParseTuple, [],
-    [AC_MSG_ERROR([Unable to find a suitable python development library])],
-    [$PYTHON_LIBS])
+AC_CHECK_FUNC([PyArg_ParseTuple], [],
+    [AC_MSG_ERROR([Unable to find a suitable python development library])]) 
+
 CPPFLAGS=$ac_previous_cppflags
 LDFLAGS=$ac_previous_ldflags
 ])
diff --git a/tools/configure b/tools/configure
index d0065b811d..e1fa5d6b0f 100755
--- a/tools/configure
+++ b/tools/configure
@@ -7418,8 +7418,6 @@ if test "$cross_compiling" != yes; then :
 
 ac_previous_cppflags=$CPPFLAGS
 ac_previous_ldflags=$LDFLAGS
-ac_python_version=`$PYTHON -c 'import distutils.sysconfig; \
-    print distutils.sysconfig.get_config_var("VERSION")'`
 # Extract the first word of "$PYTHON-config", so it can be a program name with args.
 set dummy $PYTHON-config; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -7466,24 +7464,19 @@ if test x"$pyconfig" = x"no"; then :
         CPPFLAGS="$CFLAGS `$PYTHON -c 'import distutils.sysconfig; \
         print "-I" + distutils.sysconfig.get_config_var("INCLUDEPY")'`"
     CPPFLAGS="$CPPFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("CFLAGS")'`"
-    PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("LIBS")'`"
-    PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("SYSLIBS")'`"
+        print(distutils.sysconfig.get_config_var("CFLAGS"))'`"
     LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print "-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\
-        standard_lib=1) + "/config"'`"
+        print("-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\
+        standard_lib=1) + "/config")'`"
     LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("LINKFORSHARED")'`"
+        print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`"
     LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
-        print distutils.sysconfig.get_config_var("LDFLAGS")'`"
+        print(distutils.sysconfig.get_config_var("LDFLAGS"))'`"
 
 else
 
         CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`"
     LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`"
-    PYTHON_LIBS="$LIBS `$PYTHON-config --libs`"
 
 fi
 
@@ -7495,55 +7488,14 @@ else
 fi
 
 
-as_ac_Lib=`$as_echo "ac_cv_lib_python$ac_python_version''_PyArg_ParseTuple" | $as_tr_sh`
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PyArg_ParseTuple in -lpython$ac_python_version" >&5
-$as_echo_n "checking for PyArg_ParseTuple in -lpython$ac_python_version... " >&6; }
-if eval \${$as_ac_Lib+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpython$ac_python_version $PYTHON_LIBS $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char PyArg_ParseTuple ();
-int
-main ()
-{
-return PyArg_ParseTuple ();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  eval "$as_ac_Lib=yes"
-else
-  eval "$as_ac_Lib=no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-eval ac_res=\$$as_ac_Lib
-	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
-  cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_LIBpython$ac_python_version" | $as_tr_cpp` 1
-_ACEOF
-
-  LIBS="-lpython$ac_python_version $LIBS"
+ac_fn_c_check_func "$LINENO" "PyArg_ParseTuple" "ac_cv_func_PyArg_ParseTuple"
+if test "x$ac_cv_func_PyArg_ParseTuple" = xyes; then :
 
 else
   as_fn_error $? "Unable to find a suitable python development library" "$LINENO" 5
 fi
 
+
 CPPFLAGS=$ac_previous_cppflags
 LDFLAGS=$ac_previous_ldflags
 
-- 
2.17.2


From 660d2dd863802ef464c90b32f187cb65861f8185 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Thu, 7 Mar 2019 12:33:38 +0000
Subject: [PATCH 5/8] libxl: make python scripts work with python 2.6 and up

Go through transformations suggested by 2to3 and pick the necessary
ones.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 tools/libxl/gentest.py  |  4 +++-
 tools/libxl/gentypes.py | 12 +++++++-----
 tools/libxl/idl.py      | 15 ++++++++-------
 3 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/tools/libxl/gentest.py b/tools/libxl/gentest.py
index 989959fc68..1cc7eebc82 100644
--- a/tools/libxl/gentest.py
+++ b/tools/libxl/gentest.py
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 
+from __future__ import print_function
+
 import os
 import sys
 import re
@@ -86,7 +88,7 @@ def gen_rand_init(ty, v, indent = "    ", parent = None):
 
 if __name__ == '__main__':
     if len(sys.argv) < 3:
-        print >>sys.stderr, "Usage: gentest.py <idl> <implementation>"
+        print("Usage: gentest.py <idl> <implementation>", file=sys.stderr)
         sys.exit(1)
 
     random.seed(os.getenv('LIBXL_TESTIDL_SEED'))
diff --git a/tools/libxl/gentypes.py b/tools/libxl/gentypes.py
index 88e5c5f30e..6417c9dd8c 100644
--- a/tools/libxl/gentypes.py
+++ b/tools/libxl/gentypes.py
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 
+from __future__ import print_function
+
 import sys
 import re
 
@@ -576,14 +578,14 @@ def libxl_C_enum_from_string(ty, str, e, indent = "    "):
 
 if __name__ == '__main__':
     if len(sys.argv) != 6:
-        print >>sys.stderr, "Usage: gentypes.py <idl> <header> <header-private> <header-json> <implementation>"
+        print("Usage: gentypes.py <idl> <header> <header-private> <header-json> <implementation>", file=sys.stderr)
         sys.exit(1)
 
     (_, idlname, header, header_private, header_json, impl) = sys.argv
 
     (builtins,types) = idl.parse(idlname)
 
-    print "outputting libxl type definitions to %s" % header
+    print("outputting libxl type definitions to %s" % header)
 
     f = open(header, "w")
 
@@ -633,7 +635,7 @@ if __name__ == '__main__':
     f.write("""#endif /* %s */\n""" % (header_define))
     f.close()
 
-    print "outputting libxl JSON definitions to %s" % header_json
+    print("outputting libxl JSON definitions to %s" % header_json)
 
     f = open(header_json, "w")
 
@@ -657,7 +659,7 @@ if __name__ == '__main__':
     f.write("""#endif /* %s */\n""" % header_json_define)
     f.close()
 
-    print "outputting libxl type internal definitions to %s" % header_private
+    print("outputting libxl type internal definitions to %s" % header_private)
 
     f = open(header_private, "w")
 
@@ -683,7 +685,7 @@ if __name__ == '__main__':
     f.write("""#endif /* %s */\n""" % header_json_define)
     f.close()
 
-    print "outputting libxl type implementations to %s" % impl
+    print("outputting libxl type implementations to %s" % impl)
 
     f = open(impl, "w")
     f.write("""
diff --git a/tools/libxl/idl.py b/tools/libxl/idl.py
index 2a7f3c44fe..d7367503b4 100644
--- a/tools/libxl/idl.py
+++ b/tools/libxl/idl.py
@@ -1,3 +1,5 @@
+from __future__ import print_function
+
 import sys
 
 PASS_BY_VALUE = 1
@@ -11,7 +13,7 @@ DIR_BOTH = 3
 _default_namespace = ""
 def namespace(s):
     if type(s) != str:
-        raise TypeError, "Require a string for the default namespace."
+        raise TypeError("Require a string for the default namespace.")
     global _default_namespace
     _default_namespace = s
 
@@ -346,7 +348,7 @@ class OrderedDict(dict):
         return [(x,self[x]) for x in self.__ordered]
 
 def parse(f):
-    print >>sys.stderr, "Parsing %s" % f
+    print("Parsing %s" % f, file=sys.stderr)
 
     globs = {}
     locs = OrderedDict()
@@ -362,11 +364,10 @@ def parse(f):
             globs[n] = t
 
     try:
-        execfile(f, globs, locs)
-    except SyntaxError,e:
-        raise SyntaxError, \
-              "Errors were found at line %d while processing %s:\n\t%s"\
-              %(e.lineno,f,e.text)
+        exec(compile(open(f).read(), f, 'exec'), globs, locs)
+    except SyntaxError as e:
+        raise SyntaxError("Errors were found at line %d while processing %s:\n\t%s"
+                          % (e.lineno, f, e.text))
 
     types = [t for t in locs.ordered_values() if isinstance(t,Type)]
 
-- 
2.17.2


From 0aabd89dcfee9ee2a6caaa2ec7a475daf5cada53 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Thu, 7 Mar 2019 12:45:47 +0000
Subject: [PATCH 6/8] pygrub: make python scripts work with 2.6 and up

Run 2to3 and pick the sensible suggestions.

Import print_function and absolute_import so 2.6 can work.

There has never been a curses.wrapper module according to 2.x and 3.x
doc, only a function, so "import curses.wrapper" is not correct. It
happened to work because 2.x implemented a (undocumented) module.

We only need to import curses to make curses.wrapper available to
pygrub.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 tools/pygrub/src/ExtLinuxConf.py | 19 +++++----
 tools/pygrub/src/GrubConf.py     | 39 ++++++++++--------
 tools/pygrub/src/LiloConf.py     | 19 +++++----
 tools/pygrub/src/pygrub          | 71 ++++++++++++++++----------------
 4 files changed, 78 insertions(+), 70 deletions(-)

diff --git a/tools/pygrub/src/ExtLinuxConf.py b/tools/pygrub/src/ExtLinuxConf.py
index d1789bf020..9fd635b9cf 100644
--- a/tools/pygrub/src/ExtLinuxConf.py
+++ b/tools/pygrub/src/ExtLinuxConf.py
@@ -10,9 +10,11 @@
 # along with this program; If not, see <http://www.gnu.org/licenses/>.
 #
 
+from __future__ import print_function, absolute_import
+
 import sys, re, os
 import logging
-import GrubConf
+from . import GrubConf
 
 class ExtLinuxImage(object):
     def __init__(self, lines, path):
@@ -32,7 +34,8 @@ class ExtLinuxImage(object):
         self.lines = []
         self.path = path
         self.root = ""
-        map(self.set_from_line, lines)
+        for line in lines:
+            self.set_from_line(line)
 
     def set_from_line(self, line, replace = None):
         (com, arg) = GrubConf.grub_exact_split(line, 2)
@@ -67,7 +70,7 @@ class ExtLinuxImage(object):
                         setattr(self, "initrd", a.replace("initrd=", ""))
                         arg = arg.replace(a, "")
 
-        if com is not None and self.commands.has_key(com):
+        if com is not None and com in self.commands:
             if self.commands[com] is not None:
                 setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip()))
             else:
@@ -136,7 +139,7 @@ class ExtLinuxConfigFile(object):
     def parse(self, buf = None):
         if buf is None:
             if self.filename is None:
-                raise ValueError, "No config file defined to parse!"
+                raise ValueError("No config file defined to parse!")
 
             f = open(self.filename, 'r')
             lines = f.readlines()
@@ -167,7 +170,7 @@ class ExtLinuxConfigFile(object):
 
             (com, arg) = GrubConf.grub_exact_split(l, 2)
             com = com.lower()
-            if self.commands.has_key(com):
+            if com in self.commands:
                 if self.commands[com] is not None:
                     setattr(self, self.commands[com], arg.strip())
                 else:
@@ -207,8 +210,8 @@ class ExtLinuxConfigFile(object):
         
 if __name__ == "__main__":
     if len(sys.argv) < 2:
-        raise RuntimeError, "Need a configuration file to read"
+        raise RuntimeError("Need a configuration file to read")
     g = ExtLinuxConfigFile(sys.argv[1])
     for i in g.images:
-        print i
-    print g.default
+        print(i)
+    print(g.default)
diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py
index dc810d55cb..f8d3799dc0 100644
--- a/tools/pygrub/src/GrubConf.py
+++ b/tools/pygrub/src/GrubConf.py
@@ -12,6 +12,8 @@
 # along with this program; If not, see <http://www.gnu.org/licenses/>.
 #
 
+from __future__ import print_function, absolute_import
+
 import os, sys
 import logging
 import re
@@ -44,7 +46,7 @@ def get_path(s):
         return (None, s)
     idx = s.find(')')
     if idx == -1:
-        raise ValueError, "Unable to find matching ')'"
+        raise ValueError("Unable to find matching ')'")
     d = s[:idx]
     return (GrubDiskPart(d), s[idx + 1:])
 
@@ -100,7 +102,8 @@ class _GrubImage(object):
                 "  initrd: %s\n" %(self.title, self.root, self.kernel,
                                    self.args, self.initrd))
     def _parse(self, lines):
-        map(self.set_from_line, lines)
+        for line in lines:
+            self.set_from_line(line)
 
     def reset(self, lines):
         self._root = self._initrd = self._kernel = self._args = None
@@ -141,7 +144,7 @@ class GrubImage(_GrubImage):
     def set_from_line(self, line, replace = None):
         (com, arg) = grub_exact_split(line, 2)
 
-        if self.commands.has_key(com):
+        if com in self.commands:
             if self.commands[com] is not None:
                 setattr(self, self.commands[com], arg.strip())
             else:
@@ -177,7 +180,7 @@ class _GrubConfigFile(object):
             self.parse()
 
     def parse(self, buf = None):
-        raise RuntimeError, "unimplemented parse function"   
+        raise RuntimeError("unimplemented parse function")
 
     def hasPasswordAccess(self):
         return self.passwordAccess
@@ -201,7 +204,7 @@ class _GrubConfigFile(object):
                 import crypt
                 if crypt.crypt(password, pwd[1]) == pwd[1]:
                     return True
-            except Exception, e:
+            except Exception as e:
                 self.passExc = "Can't verify password: %s" % str(e)
                 return False
 
@@ -213,7 +216,7 @@ class _GrubConfigFile(object):
 
     def set(self, line):
         (com, arg) = grub_exact_split(line, 2)
-        if self.commands.has_key(com):
+        if com in self.commands:
             if self.commands[com] is not None:
                 setattr(self, self.commands[com], arg.strip())
             else:
@@ -233,7 +236,7 @@ class _GrubConfigFile(object):
             self._default = val
 
         if self._default < 0:
-            raise ValueError, "default must be positive number"
+            raise ValueError("default must be positive number")
     default = property(_get_default, _set_default)
 
     def set_splash(self, val):
@@ -265,7 +268,7 @@ class GrubConfigFile(_GrubConfigFile):
     def parse(self, buf = None):
         if buf is None:
             if self.filename is None:
-                raise ValueError, "No config file defined to parse!"
+                raise ValueError("No config file defined to parse!")
 
             f = open(self.filename, 'r')
             lines = f.readlines()
@@ -296,7 +299,7 @@ class GrubConfigFile(_GrubConfigFile):
                 continue
 
             (com, arg) = grub_exact_split(l, 2)
-            if self.commands.has_key(com):
+            if com in self.commands:
                 if self.commands[com] is not None:
                     setattr(self, self.commands[com], arg.strip())
                 else:
@@ -328,7 +331,7 @@ class Grub2Image(_GrubImage):
         if com == "set":
             (com,arg) = grub2_handle_set(arg)
             
-        if self.commands.has_key(com):
+        if com in self.commands:
             if self.commands[com] is not None:
                 setattr(self, self.commands[com], arg.strip())
             else:
@@ -364,7 +367,7 @@ class Grub2ConfigFile(_GrubConfigFile):
     def parse(self, buf = None):
         if buf is None:
             if self.filename is None:
-                raise ValueError, "No config file defined to parse!"
+                raise ValueError("No config file defined to parse!")
 
             f = open(self.filename, 'r')
             lines = f.readlines()
@@ -398,7 +401,7 @@ class Grub2ConfigFile(_GrubConfigFile):
             title_match = re.match('^menuentry ["\'](.*?)["\'] (.*){', l)
             if title_match:
                 if img is not None:
-                    raise RuntimeError, "syntax error: cannot nest menuentry (%d %s)" % (len(img),img)
+                    raise RuntimeError("syntax error: cannot nest menuentry (%d %s)" % (len(img),img))
                 img = []
                 title = title_match.group(1)
                 continue
@@ -413,7 +416,7 @@ class Grub2ConfigFile(_GrubConfigFile):
                         menu_level -= 1
                         continue
                     else:
-                        raise RuntimeError, "syntax error: closing brace without menuentry"
+                        raise RuntimeError("syntax error: closing brace without menuentry")
 
                 self.add_image(Grub2Image(title, img))
                 img = None
@@ -428,7 +431,7 @@ class Grub2ConfigFile(_GrubConfigFile):
             if com == "set":
                 (com,arg) = grub2_handle_set(arg)
                 
-            if self.commands.has_key(com):
+            if com in self.commands:
                 if self.commands[com] is not None:
                     arg_strip = arg.strip()
                     if arg_strip == "${saved_entry}" or arg_strip == "${next_entry}":
@@ -443,7 +446,7 @@ class Grub2ConfigFile(_GrubConfigFile):
                 logging.warning("Unknown directive %s" %(com,))
             
         if img is not None:
-            raise RuntimeError, "syntax error: end of file with open menuentry(%d %s)" % (len(img),img)
+            raise RuntimeError("syntax error: end of file with open menuentry(%d %s)" % (len(img),img))
 
         if self.hasPassword():
             self.setPasswordAccess(False)
@@ -462,12 +465,12 @@ class Grub2ConfigFile(_GrubConfigFile):
         
 if __name__ == "__main__":
     if len(sys.argv) < 3:
-        raise RuntimeError, "Need a grub version (\"grub\" or \"grub2\") and a grub.conf or grub.cfg to read"
+        raise RuntimeError('Need a grub version ("grub" or "grub2") and a grub.conf or grub.cfg to read')
     if sys.argv[1] == "grub":
         g = GrubConfigFile(sys.argv[2])
     elif sys.argv[1] == "grub2":
         g = Grub2ConfigFile(sys.argv[2])
     else:
-        raise RuntimeError, "Unknown config type %s" % sys.argv[1]
+        raise RuntimeError("Unknown config type %s" % sys.argv[1])
     for i in g.images:
-        print i #, i.title, i.root, i.kernel, i.args, i.initrd
+        print(i) #, i.title, i.root, i.kernel, i.args, i.initrd
diff --git a/tools/pygrub/src/LiloConf.py b/tools/pygrub/src/LiloConf.py
index 2cb649f115..e3bfcb5244 100644
--- a/tools/pygrub/src/LiloConf.py
+++ b/tools/pygrub/src/LiloConf.py
@@ -2,9 +2,11 @@
 #LiloConf.py
 #
 
+from __future__ import print_function, absolute_import
+
 import sys, re, os
 import logging
-import GrubConf
+from . import GrubConf
 
 class LiloImage(object):
     def __init__(self, lines, path):
@@ -24,12 +26,13 @@ class LiloImage(object):
         self.lines = []
         self.path = path
         self.root = ""
-        map(self.set_from_line, lines)
+        for line in lines:
+            self.set_from_line(line)
 
     def set_from_line(self, line, replace = None):
         (com, arg) = GrubConf.grub_exact_split(line, 2)
 
-        if self.commands.has_key(com):
+        if com in self.commands:
             if self.commands[com] is not None:
                 setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip()))
             else:
@@ -97,7 +100,7 @@ class LiloConfigFile(object):
     def parse(self, buf = None):
         if buf is None:
             if self.filename is None:
-                raise ValueError, "No config file defined to parse!"
+                raise ValueError("No config file defined to parse!")
 
             f = open(self.filename, 'r')
             lines = f.readlines()
@@ -127,7 +130,7 @@ class LiloConfigFile(object):
                 continue
 
             (com, arg) = GrubConf.grub_exact_split(l, 2)
-            if self.commands.has_key(com):
+            if com in self.commands:
                 if self.commands[com] is not None:
                     setattr(self, self.commands[com], arg.strip())
                 else:
@@ -170,8 +173,8 @@ class LiloConfigFile(object):
 
 if __name__ == "__main__":
     if len(sys.argv) < 2:
-        raise RuntimeError, "Need a lilo.conf to read"
+        raise RuntimeError("Need a lilo.conf to read")
     g = LiloConfigFile(sys.argv[1])
     for i in g.images:
-        print i #, i.title, i.root, i.kernel, i.args, i.initrd
-    print g.default
+        print(i) #, i.title, i.root, i.kernel, i.args, i.initrd
+    print(g.default)
diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub
index 1189b1ca48..dbdce315c6 100755
--- a/tools/pygrub/src/pygrub
+++ b/tools/pygrub/src/pygrub
@@ -12,13 +12,15 @@
 # along with this program; If not, see <http://www.gnu.org/licenses/>.
 #
 
+from __future__ import print_function
+
 import os, sys, string, struct, tempfile, re, traceback, stat, errno
 import copy
 import logging
 import platform
 import xen.lowlevel.xc
 
-import curses, _curses, curses.wrapper, curses.textpad, curses.ascii
+import curses, _curses, curses.textpad, curses.ascii
 import getopt
 
 import xenfsimage
@@ -77,7 +79,7 @@ def get_solaris_slice(file, offset):
     buf = os.read(fd, 512)
     os.close(fd)
     if struct.unpack("<H", buf[508:510])[0] != DKL_MAGIC:
-        raise RuntimeError, "Invalid disklabel magic"
+        raise RuntimeError("Invalid disklabel magic")
 
     nslices = struct.unpack("<H", buf[30:32])[0]
 
@@ -88,7 +90,7 @@ def get_solaris_slice(file, offset):
         if slicetag == V_ROOT:
             return slicesect * SECTOR_SIZE
 
-    raise RuntimeError, "No root slice found"      
+    raise RuntimeError("No root slice found")
 
 def get_fs_offset_gpt(file):
     fd = os.open(file, os.O_RDONLY)
@@ -423,20 +425,17 @@ class Grub:
         we're being given a raw config file rather than a disk image."""
         
         if not os.access(fn, os.R_OK):
-            raise RuntimeError, "Unable to access %s" %(fn,)
+            raise RuntimeError("Unable to access %s" %(fn,))
 
-        cfg_list = map(lambda x: (x,grub.GrubConf.Grub2ConfigFile),
-                       ["/boot/grub/grub.cfg", "/grub/grub.cfg",
-                        "/boot/grub2/grub.cfg", "/grub2/grub.cfg"]) + \
-                   map(lambda x: (x,grub.ExtLinuxConf.ExtLinuxConfigFile),
-                       ["/boot/isolinux/isolinux.cfg",
+        cfg_list = [(x,grub.GrubConf.Grub2ConfigFile) for x in ["/boot/grub/grub.cfg", "/grub/grub.cfg",
+                        "/boot/grub2/grub.cfg", "/grub2/grub.cfg"]] + \
+                   [(x,grub.ExtLinuxConf.ExtLinuxConfigFile) for x in ["/boot/isolinux/isolinux.cfg",
                         "/boot/extlinux/extlinux.conf",
                         "/boot/extlinux.conf",
                         "/extlinux/extlinux.conf",
-                        "/extlinux.conf"]) + \
-                   map(lambda x: (x,grub.GrubConf.GrubConfigFile),
-                       ["/boot/grub/menu.lst", "/boot/grub/grub.conf",
-                        "/grub/menu.lst", "/grub/grub.conf"])
+                        "/extlinux.conf"]] + \
+                   [(x,grub.GrubConf.GrubConfigFile) for x in ["/boot/grub/menu.lst", "/boot/grub/grub.conf",
+                        "/grub/menu.lst", "/grub/grub.conf"]]
 
         if not fs:
             # set the config file and parse it
@@ -448,12 +447,12 @@ class Grub:
 
         for f,parser in cfg_list:
             if fs.file_exists(f):
-                print >>sys.stderr, "Using %s to parse %s" % (parser,f)
+                print("Using %s to parse %s" % (parser,f), file=sys.stderr)
                 self.cf = parser()
                 self.cf.filename = f
                 break
         if self.__dict__.get('cf', None) is None:
-            raise RuntimeError, "couldn't find bootloader config file in the image provided."
+            raise RuntimeError("couldn't find bootloader config file in the image provided.")
         f = fs.open_file(self.cf.filename)
         # limit read size to avoid pathological cases
         buf = f.read(FS_READ_MAX)
@@ -628,11 +627,11 @@ def run_grub(file, entry, fs, cfg_args):
     if list_entries:
         for i in range(len(g.cf.images)):
             img = g.cf.images[i]
-            print "title: %s" % img.title
-            print "  root: %s" % img.root
-            print "  kernel: %s" % img.kernel[1]
-            print "  args: %s" % img.args
-            print "  initrd: %s" % img.initrd[1]
+            print("title: %s" % img.title)
+            print("  root: %s" % img.root)
+            print("  kernel: %s" % img.kernel[1])
+            print("  args: %s" % img.args)
+            print("  initrd: %s" % img.initrd[1])
 
     if interactive and not list_entries:
         curses.wrapper(run_main)
@@ -646,7 +645,7 @@ def run_grub(file, entry, fs, cfg_args):
            sel = idx
 
     if sel == -1:
-        print "No kernel image selected!"
+        print("No kernel image selected!")
         sys.exit(1)
 
     try:
@@ -731,7 +730,7 @@ def format_sxp(kernel, ramdisk, args):
 def format_simple(kernel, ramdisk, args, sep):
     for check in (kernel, ramdisk, args):
         if check is not None and sep in check:
-            raise RuntimeError, "simple format cannot represent delimiter-containing value"
+            raise RuntimeError("simple format cannot represent delimiter-containing value")
     s = ("kernel %s" % kernel) + sep
     if ramdisk:
         s += ("ramdisk %s" % ramdisk) + sep
@@ -744,7 +743,7 @@ if __name__ == "__main__":
     sel = None
     
     def usage():
-        print >> sys.stderr, "Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] <image>" %(sys.argv[0],)
+        print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] <image>" %(sys.argv[0],), file=sys.stderr)
 
     def copy_from_image(fs, file_to_read, file_type, output_directory,
                         not_really):
@@ -755,8 +754,8 @@ if __name__ == "__main__":
                 sys.exit("The requested %s file does not exist" % file_type)
         try:
             datafile = fs.open_file(file_to_read)
-        except Exception, e:
-            print >>sys.stderr, e
+        except Exception as e:
+            print(e, file=sys.stderr)
             sys.exit("Error opening %s in guest" % file_to_read)
         (tfd, ret) = tempfile.mkstemp(prefix="boot_"+file_type+".",
                                       dir=output_directory)
@@ -769,8 +768,8 @@ if __name__ == "__main__":
                 return ret
             try:
                 os.write(tfd, data)
-            except Exception, e:
-                print >>sys.stderr, e
+            except Exception as e:
+                print(e, file=sys.stderr)
                 os.close(tfd)
                 os.unlink(ret)
                 del datafile
@@ -834,7 +833,7 @@ if __name__ == "__main__":
             try:
                 part_offs = [ int(a) ]
             except ValueError:
-                print "offset value must be an integer"
+                print("offset value must be an integer")
                 usage()
                 sys.exit(1)
         elif o in ("--entry",):
@@ -847,13 +846,13 @@ if __name__ == "__main__":
             debug = True
         elif o in ("--output-format",):
             if a not in ["sxp", "simple", "simple0"]:
-                print "unknown output format %s" % a
+                print("unknown output format %s" % a)
                 usage()
                 sys.exit(1)
             output_format = a
         elif o in ("--output-directory",):
             if not os.path.isdir(a):
-                print "%s is not an existing directory" % a
+                print("%s is not an existing directory" % a)
                 sys.exit(1)
             output_directory = a
 
@@ -862,8 +861,8 @@ if __name__ == "__main__":
 
 
     try:
-        os.makedirs(output_directory, 0700)
-    except OSError,e:
+        os.makedirs(output_directory, 0o700)
+    except OSError as e:
         if (e.errno == errno.EEXIST) and os.path.isdir(output_directory):
             pass
         else:
@@ -877,10 +876,10 @@ if __name__ == "__main__":
     # debug
     if isconfig:
         chosencfg = run_grub(file, entry, fs, incfg["args"])
-        print "  kernel: %s" % chosencfg["kernel"]
+        print("  kernel: %s" % chosencfg["kernel"])
         if chosencfg["ramdisk"]:
-            print "  initrd: %s" % chosencfg["ramdisk"]
-        print "  args: %s" % chosencfg["args"]
+            print("  initrd: %s" % chosencfg["ramdisk"])
+        print("  args: %s" % chosencfg["args"])
         sys.exit(0)
 
     # if boot filesystem is set then pass to fsimage.open
@@ -926,7 +925,7 @@ if __name__ == "__main__":
 
     # Did looping through partitions find us a kernel?
     if fs is None:
-        raise RuntimeError, "Unable to find partition containing kernel"
+        raise RuntimeError("Unable to find partition containing kernel")
 
     bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel",
                                         output_directory, not_really)
-- 
2.17.2


From 83a204e6951c6358f995da3b60dd61224e9d41ac Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Tue, 5 Mar 2019 14:13:17 +0000
Subject: [PATCH 7/8] pygrub/fsimage: make it work with python 3

With the help of two porting guides and cpython source code:

1. Use PyBytes to replace PyString counterparts.
2. Use PyVarObject_HEAD_INIT.
3. Remove usage of Py_FindMethod.
4. Use new module initialisation routine.

For #3, Py_FindMethod was removed, yet an alternative wasn't
documented.  The code is the result of reverse-engineering cpython
commit 6116d4a1d1

https://docs.python.org/3/howto/cporting.html
http://python3porting.com/cextensions.html

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 tools/pygrub/src/fsimage/fsimage.c | 123 ++++++++++++++++-------------
 1 file changed, 69 insertions(+), 54 deletions(-)

diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c
index 780207791c..2ebbbe35df 100644
--- a/tools/pygrub/src/fsimage/fsimage.c
+++ b/tools/pygrub/src/fsimage/fsimage.c
@@ -26,12 +26,6 @@
 #include <xenfsimage.h>
 #include <stdlib.h>
 
-#if (PYTHON_API_VERSION >= 1011)
-#define PY_PAD 0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L
-#else
-#define PY_PAD 0L,0L,0L,0L
-#endif
-
 typedef struct fsimage_fs {
 	PyObject_HEAD
 	fsi_t *fs;
@@ -59,12 +53,24 @@ fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs)
 
 	bufsize = size ? size : 4096;
 
-	if ((buffer = PyString_FromStringAndSize(NULL, bufsize)) == NULL)
+	buffer =
+#if PY_MAJOR_VERSION < 3
+		PyString_FromStringAndSize(NULL, bufsize);
+#else
+		PyBytes_FromStringAndSize(NULL, bufsize);
+#endif
+
+	if (buffer == NULL)
 		return (NULL);
  
 	while (1) {
 		int err;
-		void *buf = PyString_AS_STRING(buffer) + bytesread;
+		void *buf =
+#if PY_MAJOR_VERSION < 3
+			PyString_AS_STRING(buffer) + bytesread;
+#else
+			PyBytes_AS_STRING(buffer) + bytesread;
+#endif
 
 		err = fsi_pread_file(file->file, buf, bufsize,
 		    bytesread + offset);
@@ -84,12 +90,20 @@ fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs)
 			if (bufsize == 0)
 				break;
 		} else {
+#if PY_MAJOR_VERSION < 3
 			if (_PyString_Resize(&buffer, bytesread + bufsize) < 0)
+#else
+			if (_PyBytes_Resize(&buffer, bytesread + bufsize) < 0)
+#endif
 				return (NULL);
 		}
 	}
 
+#if PY_MAJOR_VERSION < 3
 	_PyString_Resize(&buffer, bytesread);
+#else
+	_PyBytes_Resize(&buffer, bytesread);
+#endif
 	return (buffer);
 }
 
@@ -106,11 +120,13 @@ static struct PyMethodDef fsimage_file_methods[] = {
 	{ NULL, NULL, 0, NULL }	
 };
 
+#if PY_MAJOR_VERSION < 3
 static PyObject *
 fsimage_file_getattr(fsimage_file_t *file, char *name)
 {
 	return (Py_FindMethod(fsimage_file_methods, (PyObject *)file, name));
 }
+#endif
 
 static void
 fsimage_file_dealloc(fsimage_file_t *file)
@@ -123,29 +139,18 @@ fsimage_file_dealloc(fsimage_file_t *file)
 
 static char fsimage_file_type__doc__[] = "Filesystem image file";
 PyTypeObject fsimage_file_type = {
-	PyObject_HEAD_INIT(&PyType_Type)
-	0,					/* ob_size */
-	"xenfsimage.file",			/* tp_name */
-	sizeof(fsimage_file_t),			/* tp_size */
-	0,					/* tp_itemsize */
-	(destructor) fsimage_file_dealloc, 	/* tp_dealloc */
-	0,					/* tp_print */
-	(getattrfunc) fsimage_file_getattr, 	/* tp_getattr */
-	0,					/* tp_setattr */
-	0,					/* tp_compare */
-	0,					/* tp_repr */
-	0,					/* tp_as_number */
-	0,	 				/* tp_as_sequence */
-	0,					/* tp_as_mapping */
-	0,	   				/* tp_hash */
-	0,					/* tp_call */
-	0,					/* tp_str */
-	0,					/* tp_getattro */
-	0,					/* tp_setattro */
-	0,					/* tp_as_buffer */
-	Py_TPFLAGS_DEFAULT,			/* tp_flags */
-	fsimage_file_type__doc__,
-	PY_PAD
+	PyVarObject_HEAD_INIT(&PyType_Type, 0)
+	.tp_name = "xenfsimage.file",
+	.tp_basicsize = sizeof(fsimage_file_t),
+	.tp_dealloc = (destructor) fsimage_file_dealloc,
+#if PY_MAJOR_VERSION < 3
+	.tp_getattr = (getattrfunc) fsimage_file_getattr,
+#endif
+	.tp_flags = Py_TPFLAGS_DEFAULT,
+	.tp_doc = fsimage_file_type__doc__,
+#if PY_MAJOR_VERSION >= 3
+	.tp_methods = fsimage_file_methods,
+#endif
 };
 
 static PyObject *
@@ -208,11 +213,13 @@ static struct PyMethodDef fsimage_fs_methods[] = {
 	{ NULL, NULL, 0, NULL }	
 };
 
+#if PY_MAJOR_VERSION < 3
 static PyObject *
 fsimage_fs_getattr(fsimage_fs_t *fs, char *name)
 {
 	return (Py_FindMethod(fsimage_fs_methods, (PyObject *)fs, name));
 }
+#endif
 
 static void
 fsimage_fs_dealloc (fsimage_fs_t *fs)
@@ -225,29 +232,18 @@ fsimage_fs_dealloc (fsimage_fs_t *fs)
 PyDoc_STRVAR(fsimage_fs_type__doc__, "Filesystem image");
 
 PyTypeObject fsimage_fs_type = {
-	PyObject_HEAD_INIT(&PyType_Type)
-	0,					/* ob_size */
-	"xenfsimage.fs",			/* tp_name */
-	sizeof(fsimage_fs_t),			/* tp_size */
-	0,					/* tp_itemsize */
-	(destructor) fsimage_fs_dealloc, 	/* tp_dealloc */
-	0,					/* tp_print */
-	(getattrfunc) fsimage_fs_getattr, 	/* tp_getattr */
-	0,					/* tp_setattr */
-	0,					/* tp_compare */
-	0,					/* tp_repr */
-	0,					/* tp_as_number */
-	0,	 				/* tp_as_sequence */
-	0,					/* tp_as_mapping */
-	0,	   				/* tp_hash */
-	0,					/* tp_call */
-	0,					/* tp_str */
-	0,					/* tp_getattro */
-	0,					/* tp_setattro */
-	0,					/* tp_as_buffer */
-	Py_TPFLAGS_DEFAULT,			/* tp_flags */
-	fsimage_fs_type__doc__,
-	PY_PAD
+	PyVarObject_HEAD_INIT(&PyType_Type, 0)
+	.tp_name = "xenfsimage.fs",
+	.tp_basicsize = sizeof(fsimage_fs_t),
+	.tp_dealloc = (destructor) fsimage_fs_dealloc,
+#if PY_MAJOR_VERSION < 3
+	.tp_getattr = (getattrfunc) fsimage_fs_getattr,
+#endif
+	.tp_flags = Py_TPFLAGS_DEFAULT,
+	.tp_doc = fsimage_fs_type__doc__,
+#if PY_MAJOR_VERSION >= 3
+	.tp_methods = fsimage_fs_methods,
+#endif
 };
 
 static PyObject *
@@ -309,8 +305,27 @@ static struct PyMethodDef fsimage_module_methods[] = {
 	{ NULL, NULL, 0, NULL }
 };
 
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef fsimage_module_def = {
+	PyModuleDef_HEAD_INIT,
+	.m_name = "xenfsimage",
+	.m_size = -1,
+	.m_methods = fsimage_module_methods,
+};
+#endif
+
 PyMODINIT_FUNC
+#if PY_MAJOR_VERSION >= 3
+PyInit_xenfsimage(void)
+#else
 initxenfsimage(void)
+#endif
 {
+#if PY_MAJOR_VERSION < 3
 	Py_InitModule("xenfsimage", fsimage_module_methods);
+#else
+	if (PyType_Ready(&fsimage_fs_type) < 0 || PyType_Ready(&fsimage_file_type) < 0)
+		return NULL;
+	return PyModule_Create(&fsimage_module_def);
+#endif
 }
-- 
2.17.2


From 9b0bc91b3b32856df014fab9de40f463c89a8b1e Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Mon, 11 Mar 2019 17:19:19 +0000
Subject: [PATCH 8/8] README: remove requirement on Python 2

Now that all python scripts are compatible with Python 2.6 and above,
remove the restriction.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 README | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/README b/README
index d00715c2fd..23e4f7c3dc 100644
--- a/README
+++ b/README
@@ -46,7 +46,7 @@ provided by your OS distributor:
         - GCC 4.8 or later
         - GNU Binutils 2.24 or later
     * Development install of zlib (e.g., zlib-dev)
-    * Development install of Python 2, v2.6 or later (e.g., python-dev)
+    * Development install of Python 2.6 or later (e.g., python-dev)
     * Development install of curses (e.g., libncurses-dev)
     * Development install of openssl (e.g., openssl-dev)
     * Development install of x11 (e.g. xorg-x11-dev)
@@ -177,16 +177,10 @@ Python Runtime Libraries
 
 Various tools, such as pygrub, have the following runtime dependencies:
 
-    * Python 2, v2.6 or later.
+    * Python 2.6 or later.
           URL:    http://www.python.org/
           Debian: python
 
-Note that the build system expects `python` to be python2.  If your system
-has `python` pointing to python3 (as in the case of Arch Linux or Anaconda),
-you'll need to specify a path to a python2 binary when running configure:
-
-    PYTHON=/usr/bin/python2 ./configure
-
 Intel(R) Trusted Execution Technology Support
 =============================================
 
-- 
2.17.2

From 38a85fe8d98b54da7f842f8d78bf8c54f7747735 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Wed, 13 Mar 2019 13:54:48 +0000
Subject: [PATCH] build/m4: fix python library detection on Ubuntu systems

16cc3362aed doesn't work on Ubuntu with gcc (but it does work with
clang). Work around it by manipulating LIBS.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 m4/python_devel.m4 | 7 +++++++
 tools/configure    | 7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/m4/python_devel.m4 b/m4/python_devel.m4
index f9cb23aee1..7f26381376 100644
--- a/m4/python_devel.m4
+++ b/m4/python_devel.m4
@@ -1,6 +1,7 @@
 AC_DEFUN([AX_CHECK_PYTHON_DEVEL], [
 ac_previous_cppflags=$CPPFLAGS
 ac_previous_ldflags=$LDFLAGS
+ac_previous_libs=$LIBS
 AC_PATH_PROG([pyconfig], [$PYTHON-config], [no])
 AS_IF([test x"$pyconfig" = x"no"], [
     dnl For those that don't have python-config
@@ -15,10 +16,15 @@ AS_IF([test x"$pyconfig" = x"no"], [
         print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`"
     LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
         print(distutils.sysconfig.get_config_var("LDFLAGS"))'`"
+    LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \
+        print(distutils.sysconfig.get_config_var("LIBS"))'`"
+    LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \
+        print(distutils.sysconfig.get_config_var("SYSLIBS"))'`"
 ], [
     dnl If python-config is found use it
     CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`"
     LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`"
+    LIBS="$LIBS `$PYTHON-config --libs`"
 ])
 
 AC_CHECK_HEADER([Python.h], [],
@@ -28,4 +34,5 @@ AC_CHECK_FUNC([PyArg_ParseTuple], [],
 
 CPPFLAGS=$ac_previous_cppflags
 LDFLAGS=$ac_previous_ldflags
+LIBS=$ac_previous_libs
 ])
diff --git a/tools/configure b/tools/configure
index 9ff879548a..b66d3f6fba 100755
--- a/tools/configure
+++ b/tools/configure
@@ -7418,6 +7418,7 @@ if test "$cross_compiling" != yes; then :
 
 ac_previous_cppflags=$CPPFLAGS
 ac_previous_ldflags=$LDFLAGS
+ac_previous_libs=$LIBS
 # Extract the first word of "$PYTHON-config", so it can be a program name with args.
 set dummy $PYTHON-config; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -7472,11 +7473,16 @@ if test x"$pyconfig" = x"no"; then :
         print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`"
     LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \
         print(distutils.sysconfig.get_config_var("LDFLAGS"))'`"
+    LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \
+        print(distutils.sysconfig.get_config_var("LIBS"))'`"
+    LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \
+        print(distutils.sysconfig.get_config_var("SYSLIBS"))'`"
 
 else
 
         CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`"
     LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`"
+    LIBS="$LIBS `$PYTHON-config --libs`"
 
 fi
 
@@ -7498,6 +7504,7 @@ fi
 
 CPPFLAGS=$ac_previous_cppflags
 LDFLAGS=$ac_previous_ldflags
+LIBS=$ac_previous_libs
 
 
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Python setup.py brokenly enables -D_FORTIFY_SOURCE" >&5
-- 
2.17.2

From 485079e816788d70169f45579e1f5a8f909dc1b3 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Mon, 1 Apr 2019 11:32:35 +0100
Subject: [PATCH 1/4] pygrub: fix message in grub parser

The code suggests 0 is allowed. Zero is not a positive number.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 tools/pygrub/src/GrubConf.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py
index f8d3799dc0..0204d410ac 100644
--- a/tools/pygrub/src/GrubConf.py
+++ b/tools/pygrub/src/GrubConf.py
@@ -236,7 +236,7 @@ class _GrubConfigFile(object):
             self._default = val
 
         if self._default < 0:
-            raise ValueError("default must be positive number")
+            raise ValueError("default must be non-negative number")
     default = property(_get_default, _set_default)
 
     def set_splash(self, val):
-- 
2.17.2


From 767ba397d34848c7e0c4e9cdfc5efa4e0cb61442 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Mon, 1 Apr 2019 11:32:36 +0100
Subject: [PATCH 2/4] pygrub/grub: always use integer for default entry

The original code set the default to either a string or an integer
(0) and relies on a Python 2 specific behaviour to work (integer is
allowed to be compared to string in Python 2 but not 3).

Always use integer. The caller (pygrub) already has code to handle
that.

Reported-by: M A Young <m.a.young@durham.ac.uk>
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 tools/pygrub/src/GrubConf.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py
index 0204d410ac..594139bac7 100644
--- a/tools/pygrub/src/GrubConf.py
+++ b/tools/pygrub/src/GrubConf.py
@@ -233,7 +233,11 @@ class _GrubConfigFile(object):
         if val == "saved":
             self._default = 0
         else:
-            self._default = val
+            try:
+                self._default = int(val)
+            except ValueError:
+                logging.warning("Invalid value %s, setting default to 0" %(val,))
+                self._default = 0
 
         if self._default < 0:
             raise ValueError("default must be non-negative number")
-- 
2.17.2


From ff915c8cacc264ae1380d51fea07267b8308d7ba Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Mon, 1 Apr 2019 11:32:37 +0100
Subject: [PATCH 3/4] pygrub: encode / decode string in Python 3

String is unicode in 3 but bytes in 2. We need to call encode / decode
function when using Python 3.

Reported-by: M A Young <m.a.young@durham.ac.uk>
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
 tools/pygrub/src/pygrub | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub
index dbdce315c6..ce7ab0eb8c 100755
--- a/tools/pygrub/src/pygrub
+++ b/tools/pygrub/src/pygrub
@@ -457,7 +457,10 @@ class Grub:
         # limit read size to avoid pathological cases
         buf = f.read(FS_READ_MAX)
         del f
-        self.cf.parse(buf)
+        if sys.version_info[0] < 3:
+            self.cf.parse(buf)
+        else:
+            self.cf.parse(buf.decode())
 
     def image_index(self):
         if isinstance(self.cf.default, int):
@@ -960,5 +963,8 @@ if __name__ == "__main__":
         ostring = format_simple(bootcfg["kernel"], bootcfg["ramdisk"], args, "\0")
 
     sys.stdout.flush()
-    os.write(fd, ostring)
+    if sys.version_info[0] < 3:
+        os.write(fd, ostring)
+    else:
+        os.write(fd, ostring.encode())
     
-- 
2.17.2


From a57a1b26ec0ae31f924cf2bbcf479637d007be44 Mon Sep 17 00:00:00 2001
From: Wei Liu <wei.liu2@citrix.com>
Date: Mon, 1 Apr 2019 11:32:38 +0100
Subject: [PATCH 4/4] tools/ocaml: make python scripts 2 and 3 compatible

1. Explicitly import reduce because that's required in 3.
2. Change print to function.
3. Eliminate invocations of has_key.

Signed-off-by: M A Young <m.a.young@durham.ac.uk>
Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Christian Lindig <christian.lindig@citrix.com>
---
 tools/ocaml/libs/xentoollog/genlevels.py |  5 ++++-
 tools/ocaml/libs/xl/genwrap.py           | 17 ++++++++++-------
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/tools/ocaml/libs/xentoollog/genlevels.py b/tools/ocaml/libs/xentoollog/genlevels.py
index 8c233c59b1..f9cf853e26 100755
--- a/tools/ocaml/libs/xentoollog/genlevels.py
+++ b/tools/ocaml/libs/xentoollog/genlevels.py
@@ -1,6 +1,9 @@
 #!/usr/bin/python
 
+from __future__ import print_function
+
 import sys
+from functools import reduce
 
 def read_levels():
 	f = open('../../../libs/toollog/include/xentoollog.h', 'r')
@@ -93,7 +96,7 @@ def autogen_header(open_comment, close_comment):
 
 if __name__ == '__main__':
 	if len(sys.argv) < 3:
-		print >>sys.stderr, "Usage: genlevels.py <mli> <ml> <c-inc>"
+		print("Usage: genlevels.py <mli> <ml> <c-inc>", file=sys.stderr)
 		sys.exit(1)
 
 	levels, olevels = read_levels()
diff --git a/tools/ocaml/libs/xl/genwrap.py b/tools/ocaml/libs/xl/genwrap.py
index 815c1cb0e3..7bf26bdcd8 100644
--- a/tools/ocaml/libs/xl/genwrap.py
+++ b/tools/ocaml/libs/xl/genwrap.py
@@ -1,6 +1,9 @@
 #!/usr/bin/python
 
+from __future__ import print_function
+
 import sys,os
+from functools import reduce
 
 import idl
 
@@ -78,7 +81,7 @@ def ocaml_type_of(ty):
     elif isinstance(ty,idl.Array):
         return "%s array" % ocaml_type_of(ty.elem_type)
     elif isinstance(ty,idl.Builtin):
-        if not builtins.has_key(ty.typename):
+        if ty.typename not in builtins:
             raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty)))
         typename,_,_ = builtins[ty.typename]
         if not typename:
@@ -251,7 +254,7 @@ def gen_ocaml_ml(ty, interface, indent=""):
             else:
                 s += "\texternal default : ctx -> %sunit -> t = \"stub_libxl_%s_init\"\n" % (union_args, ty.rawname)
 
-        if functions.has_key(ty.rawname):
+        if ty.rawname in functions:
             for name,args in functions[ty.rawname]:
                 s += "\texternal %s : " % name
                 s += " -> ".join(args)
@@ -278,7 +281,7 @@ def c_val(ty, c, o, indent="", parent = None):
         else:
             s += "%s = Int_val(%s);" % (c, o)
     elif isinstance(ty,idl.Builtin):
-        if not builtins.has_key(ty.typename):
+        if ty.typename not in builtins:
             raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty)))
         _,fn,_ = builtins[ty.typename]
         if not fn:
@@ -375,7 +378,7 @@ def ocaml_Val(ty, o, c, indent="", parent = None):
         else:
             s += "%s = Val_int(%s);" % (o, c)
     elif isinstance(ty,idl.Builtin):
-        if not builtins.has_key(ty.typename):
+        if ty.typename not in builtins:
             raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty)))
         _,_,fn = builtins[ty.typename]
         if not fn:
@@ -520,7 +523,7 @@ def autogen_header(open_comment, close_comment):
 
 if __name__ == '__main__':
     if len(sys.argv) < 4:
-        print >>sys.stderr, "Usage: genwrap.py <idl> <mli> <ml> <c-inc>"
+        print("Usage: genwrap.py <idl> <mli> <ml> <c-inc>", file=sys.stderr)
         sys.exit(1)
 
     (_,types) = idl.parse(sys.argv[1])
@@ -533,7 +536,7 @@ if __name__ == '__main__':
 
     for t in blacklist:
         if t not in [ty.rawname for ty in types]:
-            print "unknown type %s in blacklist" % t
+            print("unknown type %s in blacklist" % t)
 
     types = [ty for ty in types if not ty.rawname in blacklist]
 
@@ -564,7 +567,7 @@ if __name__ == '__main__':
             cinc.write("\n")
         cinc.write(gen_Val_ocaml(ty))
         cinc.write("\n")
-        if functions.has_key(ty.rawname):
+        if ty.rawname in functions:
             cinc.write(gen_c_stub_prototype(ty, functions[ty.rawname]))
             cinc.write("\n")
         if ty.init_fn is not None:
-- 
2.17.2