From 207402a20ac8be74e767876f8ac93aba6292b2a9 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 19 Nov 2011 16:54:26 +0000 Subject: [PATCH] python: Support Python 3 (RHBZ#752916). --- .gitignore | 1 + configure.ac | 55 ++++++++++++++++++++------- generator/generator.ml | 97 ++++++++++++++++++++++++++++++++++++------------ python/Makefile.am | 2 +- python/t/210-setvalue.py | 13 ++++++- 5 files changed, 128 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index 40a4780..820130e 100644 --- a/.gitignore +++ b/.gitignore @@ -97,6 +97,7 @@ po/insert-header.sin po/quot.sed po/remove-potcdate.sin python/*.pyc +python/__pycache__/ python/hivex-py.c python/hivex.py python/run-python-tests diff --git a/configure.ac b/configure.ac index 547bf0a..004efaa 100644 --- a/configure.ac +++ b/configure.ac @@ -241,14 +241,23 @@ AM_CONDITIONAL([HAVE_PERL], [test "x$PERL" != "xno" && test "x$missing_perl_modules" != "xyes"]) dnl Check for Python (optional, for Python bindings). -AC_CHECK_PROG([PYTHON],[python],[python],[no]) - PYTHON_PREFIX= PYTHON_VERSION= +PYTHON_INCLUDEDIR= +PYTHON_INSTALLDIR= + +AC_CHECK_PROG([PYTHON],[python],[python],[no]) if test "x$PYTHON" != "xno"; then - PYTHON_PREFIX=`$PYTHON -c "import sys; print(sys.prefix)"` - PYTHON_VERSION=`$PYTHON -c "import sys; print(sys.version[[0:3]])"` + AC_MSG_CHECKING([Python prefix]) + PYTHON_PREFIX=`$PYTHON -c "import sys; print (sys.prefix)"` + AC_MSG_RESULT([$PYTHON_PREFIX]) + + AC_MSG_CHECKING([Python version]) + PYTHON_VERSION_MAJOR=`$PYTHON -c "import sys; print (sys.version_info@<:@0@:>@)"` + PYTHON_VERSION_MINOR=`$PYTHON -c "import sys; print (sys.version_info@<:@1@:>@)"` + PYTHON_VERSION="$PYTHON_VERSION_MAJOR.$PYTHON_VERSION_MINOR" + AC_MSG_RESULT([$PYTHON_VERSION]) AC_MSG_CHECKING([for Python include path]) if test -z "$PYTHON_INCLUDEDIR"; then @@ -258,26 +267,46 @@ if test "x$PYTHON" != "xno"; then fi AC_MSG_RESULT([$PYTHON_INCLUDEDIR]) - AC_MSG_CHECKING([for Python site-packages path]) - if test -z "$PYTHON_SITE_PACKAGES"; then - PYTHON_SITE_PACKAGES=`$PYTHON -c "import distutils.sysconfig; \ - print (distutils.sysconfig.get_python_lib(1,0));"` + AC_ARG_WITH([python-installdir], + [AS_HELP_STRING([--with-python-installdir], + [directory to install python modules @<:@default=check@:>@])], + [PYTHON_INSTALLDIR="$withval" + AC_MSG_NOTICE([Python install dir $PYTHON_INSTALLDIR])], + [PYTHON_INSTALLDIR=check]) + + if test "x$PYTHON_INSTALLDIR" = "xcheck"; then + PYTHON_INSTALLDIR= + AC_MSG_CHECKING([for Python site-packages path]) + if test -z "$PYTHON_INSTALLDIR"; then + PYTHON_INSTALLDIR=`$PYTHON -c "import distutils.sysconfig; \ + print (distutils.sysconfig.get_python_lib(1,0));"` + fi + AC_MSG_RESULT([$PYTHON_INSTALLDIR]) fi - AC_MSG_RESULT([$PYTHON_SITE_PACKAGES]) + dnl Look for libpython and some optional symbols in it. old_LIBS="$LIBS" - LIBS="$LIBS -lpython$PYTHON_VERSION" - AC_CHECK_FUNCS([PyCapsule_New]) + if test "x$PYTHON_VERSION_MAJOR" = "x3"; then + dnl libpython3 is called "libpython3.Xmu.so" + LIBPYTHON="python${PYTHON_VERSION}mu" + else + LIBPYTHON="python$PYTHON_VERSION" + fi + AC_CHECK_LIB([$LIBPYTHON], [PyList_Size], [], + [AC_MSG_FAILURE([$LIBPYTHON is not installed])]) + + AC_CHECK_FUNCS([PyCapsule_New \ + PyString_AsString]) LIBS="$old_LIBS" fi AC_SUBST(PYTHON_PREFIX) AC_SUBST(PYTHON_VERSION) AC_SUBST(PYTHON_INCLUDEDIR) -AC_SUBST(PYTHON_SITE_PACKAGES) +AC_SUBST(PYTHON_INSTALLDIR) AM_CONDITIONAL([HAVE_PYTHON], - [test "x$PYTHON_INCLUDEDIR" != "x" && test "x$PYTHON_SITE_PACKAGES" != "x"]) + [test "x$PYTHON" != "xno" && test "x$PYTHON_INCLUDEDIR" != "x" && test "x$PYTHON_INSTALLDIR" != "x"]) dnl Check for Ruby and rake (optional, for Ruby bindings). AC_CHECK_LIB([ruby],[ruby_init],[HAVE_LIBRUBY=1],[HAVE_LIBRUBY=0]) diff --git a/generator/generator.ml b/generator/generator.ml index def516f..065c25d 100755 --- a/generator/generator.ml +++ b/generator/generator.ml @@ -2694,6 +2694,8 @@ and generate_python_c () = generate_header CStyle LGPLv2plus; pr "\ +#include + #define PY_SSIZE_T_CLEAN 1 #include @@ -2747,40 +2749,42 @@ static int get_value (PyObject *v, hive_set_value *ret) { PyObject *obj; +#ifndef HAVE_PYSTRING_ASSTRING + PyObject *bytes; +#endif obj = PyDict_GetItemString (v, \"key\"); if (!obj) { PyErr_SetString (PyExc_RuntimeError, \"no 'key' element in dictionary\"); return -1; } - if (!PyString_Check (obj)) { - PyErr_SetString (PyExc_RuntimeError, \"'key' element is not a string\"); - return -1; - } +#ifdef HAVE_PYSTRING_ASSTRING ret->key = PyString_AsString (obj); +#else + bytes = PyUnicode_AsUTF8String (obj); + ret->key = PyBytes_AS_STRING (bytes); +#endif obj = PyDict_GetItemString (v, \"t\"); if (!obj) { PyErr_SetString (PyExc_RuntimeError, \"no 't' element in dictionary\"); return -1; } - if (!PyInt_Check (obj)) { - PyErr_SetString (PyExc_RuntimeError, \"'t' element is not an integer\"); - return -1; - } - ret->t = PyInt_AsLong (obj); + ret->t = PyLong_AsLong (obj); obj = PyDict_GetItemString (v, \"value\"); if (!obj) { PyErr_SetString (PyExc_RuntimeError, \"no 'value' element in dictionary\"); return -1; } - if (!PyString_Check (obj)) { - PyErr_SetString (PyExc_RuntimeError, \"'value' element is not a string\"); - return -1; - } +#ifdef HAVE_PYSTRING_ASSTRING ret->value = PyString_AsString (obj); ret->len = PyString_Size (obj); +#else + bytes = PyUnicode_AsUTF8String (obj); + ret->value = PyBytes_AS_STRING (bytes); + ret->len = PyBytes_GET_SIZE (bytes); +#endif return 0; } @@ -2834,8 +2838,13 @@ put_string_list (char * const * const argv) ; list = PyList_New (argc); - for (i = 0; i < argc; ++i) + for (i = 0; i < argc; ++i) { +#ifdef HAVE_PYSTRING_ASSTRING PyList_SetItem (list, i, PyString_FromString (argv[i])); +#else + PyList_SetItem (list, i, PyUnicode_FromString (argv[i])); +#endif + } return list; } @@ -2871,7 +2880,7 @@ static PyObject * put_len_type (size_t len, hive_type t) { PyObject *r = PyTuple_New (2); - PyTuple_SetItem (r, 0, PyInt_FromLong ((long) t)); + PyTuple_SetItem (r, 0, PyLong_FromLong ((long) t)); PyTuple_SetItem (r, 1, PyLong_FromLongLong ((long) len)); return r; } @@ -2880,8 +2889,12 @@ static PyObject * put_val_type (char *val, size_t len, hive_type t) { PyObject *r = PyTuple_New (2); - PyTuple_SetItem (r, 0, PyInt_FromLong ((long) t)); + PyTuple_SetItem (r, 0, PyLong_FromLong ((long) t)); +#ifdef HAVE_PYSTRING_ASSTRING PyTuple_SetItem (r, 1, PyString_FromStringAndSize (val, len)); +#else + PyTuple_SetItem (r, 1, PyBytes_FromStringAndSize (val, len)); +#endif return r; } @@ -3077,7 +3090,11 @@ put_val_type (char *val, size_t len, hive_type t) | RValue -> pr " py_r = PyLong_FromLongLong (r);\n" | RString -> + pr "#ifdef HAVE_PYSTRING_ASSTRING\n"; pr " py_r = PyString_FromString (r);\n"; + pr "#else\n"; + pr " py_r = PyUnicode_FromString (r);\n"; + pr "#endif\n"; pr " free (r);" | RStringList -> pr " py_r = put_string_list (r);\n"; @@ -3088,7 +3105,7 @@ put_val_type (char *val, size_t len, hive_type t) pr " py_r = put_val_type (r, len, t);\n"; pr " free (r);\n" | RInt32 -> - pr " py_r = PyInt_FromLong ((long) r);\n" + pr " py_r = PyLong_FromLong ((long) r);\n" | RInt64 -> pr " py_r = PyLong_FromLongLong (r);\n" ); @@ -3110,22 +3127,54 @@ put_val_type (char *val, size_t len, hive_type t) (* Init function. *) pr "\ +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + \"libhivexmod\", /* m_name */ + \"hivex module\", /* m_doc */ + -1, /* m_size */ + methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#endif + +static PyObject * +moduleinit (void) +{ + PyObject *m; + +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create (&moduledef); +#else + m = Py_InitModule ((char *) \"libhivexmod\", methods); +#endif + + return m; /* m might be NULL if module init failed */ +} + +#if PY_MAJOR_VERSION >= 3 +PyMODINIT_FUNC +PyInit_libhivexmod (void) +{ + return moduleinit (); +} +#else void initlibhivexmod (void) { - static int initialized = 0; - - if (initialized) return; - Py_InitModule ((char *) \"libhivexmod\", methods); - initialized = 1; + (void) moduleinit (); } +#endif " and generate_python_py () = generate_header HashStyle LGPLv2plus; pr "\ -u\"\"\"Python bindings for hivex +\"\"\"Python bindings for hivex import hivex h = hivex.Hivex (filename) @@ -3178,7 +3227,7 @@ class Hivex: pr " def %s (self" name; List.iter (fun arg -> pr ", %s" (name_of_argt arg)) args; pr "):\n"; - pr " u\"\"\"%s\"\"\"\n" shortdesc; + pr " \"\"\"%s\"\"\"\n" shortdesc; pr " return libhivexmod.%s (self._o" name; List.iter ( fun arg -> diff --git a/python/Makefile.am b/python/Makefile.am index 89e66f8..be1523e 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -26,7 +26,7 @@ EXTRA_DIST = \ if HAVE_PYTHON -pythondir = $(PYTHON_SITE_PACKAGES) +pythondir = $(PYTHON_INSTALLDIR) python_DATA = hivex.py diff --git a/python/t/210-setvalue.py b/python/t/210-setvalue.py index 1cb8c52..9d93519 100644 --- a/python/t/210-setvalue.py +++ b/python/t/210-setvalue.py @@ -15,6 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +import sys import os import hivex @@ -46,12 +47,20 @@ h.node_set_value (b, value1) value1 = { "key": "Key1", "t": 3, "value": "JKL" } h.node_set_value (b, value1) +# In Python2, the data is returned as a string. In Python3, it is +# returned as bytes. Provide a function to convert either to a string. +def to_string (data): + if sys.version_info[0] == 2: + return data + else: + return str (data, "utf-8") + val = h.node_get_value (b, "Key1") t_data = h.value_value (val) assert t_data[0] == 3 -assert t_data[1] == "JKL" +assert to_string (t_data[1]) == "JKL" val = h.node_get_value (b, "Key3") t_data = h.value_value (val) assert t_data[0] == 3 -assert t_data[1] == "GHI" +assert to_string (t_data[1]) == "GHI" -- 1.8.3.1