[nautilus-python] Add support for building with python 3



commit 12687a3c107c7141eaf46da7752e6b82d669f23b
Author: Adam Plumb <adamplumb gmail com>
Date:   Mon Jan 1 14:43:50 2018 -0500

    Add support for building with python 3

 configure.in                                       |    5 +-
 docs/reference/nautilus-python-column-provider.xml |   11 +++-
 docs/reference/nautilus-python-info-provider.xml   |   13 +++++-
 docs/reference/nautilus-python-overview.xml        |   16 ++++++-
 .../nautilus-python-property-page-provider.xml     |   22 +++++-----
 examples/block-size-column.py                      |    9 +++-
 examples/md5sum-property-page.py                   |   18 ++++----
 examples/open-terminal.py                          |    4 +-
 examples/update-file-info-async.py                 |   13 ++++-
 src/Makefile.am                                    |    1 +
 src/nautilus-python-object.c                       |   48 +++++++++++++++++---
 src/nautilus-python.c                              |   16 +++++--
 12 files changed, 131 insertions(+), 45 deletions(-)
---
diff --git a/configure.in b/configure.in
index 4da69ee..6d094a2 100644
--- a/configure.in
+++ b/configure.in
@@ -35,8 +35,8 @@ AC_PREFIX_DEFAULT([$(pkg-config --variable=prefix libnautilus-extension || echo
 dnl **************************************************
 dnl * Check for Python
 dnl **************************************************
-AM_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(could not find Python headers)])
-AM_CHECK_PYTHON_LIBS(,[AC_MSG_ERROR(could not find Python lib)])
+PYG_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(could not find Python headers)])
+PYG_CHECK_PYTHON_LIBS(,[AC_MSG_ERROR(could not find Python lib)])
 
 if test "`pkg-config --variable=datadir pygobject-3.0`" != "" ; then
     PYGOBJECT_VERSION=pygobject-3.0
@@ -98,5 +98,6 @@ echo " nautilus-python $VERSION"
 echo
 echo "    Nautilus Prefix: ${prefix}"
 echo "  PyGObject Version: ${PYGOBJECT_VERSION}"
+echo "     Python Library: ${PYTHON_LIB_LOC}/${PYTHON_LIB_NAME}"
 echo "      Documentation: ${enable_gtk_doc}"
 echo
diff --git a/docs/reference/nautilus-python-column-provider.xml 
b/docs/reference/nautilus-python-column-provider.xml
index edba0da..8adf2e3 100644
--- a/docs/reference/nautilus-python-column-provider.xml
+++ b/docs/reference/nautilus-python-column-provider.xml
@@ -46,9 +46,14 @@
     <title>Nautilus.ColumnProvider Example</title>
     <programlisting>
 import os
-import urllib
 
-from gi.repository import Nautilus, GObject
+# A way to get unquote working with python 2 and 3
+try:
+    from urllib import unquote
+except ImportError:
+    from urllib.parse import unquote
+
+from gi.repository import GObject, Nautilus
 
 class ColumnExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
     def __init__(self):
@@ -64,7 +69,7 @@ class ColumnExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoPro
         if file.get_uri_scheme() != 'file':
             return
         
-        filename = urllib.unquote(file.get_uri()[7:])
+        filename = unquote(file.get_uri()[7:])
         
         file.add_string_attribute('block_size', str(os.stat(filename).st_blksize))
     </programlisting>
diff --git a/docs/reference/nautilus-python-info-provider.xml 
b/docs/reference/nautilus-python-info-provider.xml
index ed156a0..74e1e42 100644
--- a/docs/reference/nautilus-python-info-provider.xml
+++ b/docs/reference/nautilus-python-info-provider.xml
@@ -65,16 +65,25 @@
     <programlisting>
 from gi.repository import Nautilus, GObject
 
-class ColumnExtension(GObject.GObject, Nautilus.InfoProvider):
+class UpdateFileInfoAsync(GObject.GObject, Nautilus.InfoProvider):
     def __init__(self):
+        self.timers = []
         pass
     
     def update_file_info_full(self, provider, handle, closure, file):
-        gobject.timeout_add_seconds(3, self.update_cb, provider, handle, closure)
+        print("update_file_info_full")
+        self.timers.append(GObject.timeout_add_seconds(3, self.update_cb, provider, handle, closure))
         return Nautilus.OperationResult.IN_PROGRESS
         
     def update_cb(self, provider, handle, closure):
+        print("update_cb")
         Nautilus.info_provider_update_complete_invoke(closure, provider, handle, 
Nautilus.OperationResult.FAILED)
+
+    def cancel_update(self, provider, handle):
+        print("cancel_update")
+        for t in self.timers:
+            GObject.source_remove(t)
+        self.timers = []
     </programlisting>
 </example>
         
diff --git a/docs/reference/nautilus-python-overview.xml b/docs/reference/nautilus-python-overview.xml
index 5f83fc4..9827d78 100644
--- a/docs/reference/nautilus-python-overview.xml
+++ b/docs/reference/nautilus-python-overview.xml
@@ -22,11 +22,20 @@
     the main class from a Nautilus module class will be loaded</para>
     
     <note>
-<title>A note about the standard python extensions install path</title>
+<title>Extension Install Path for &lt; v1.2.0</title>
 
 <para>As of nautilus-python 0.7.0 (and continued in 1.0+), nautilus-python looks in 
~/.local/share/nautilus-python/extensions 
 for local extensions and $PREFIX/share/nautilus-python/extensions for global extensions.</para>
     </note>
+
+    <note>
+<title>Extension Install Path for &gt;= v1.2.0</title>
+<para>As of nautilus-python 1.2.0, python extensions are loaded in the following order:
+
+  1. $XDG_DATA_HOME/nautilus-python/extensions
+  2. nautilus_prefix/share/nautilus-python/extensions
+  3. $XDG_DATA_DIRS/nautilus-python/extensions</para>
+    </note>
     
     <note>
 <title>A note about compatibility issues for nautilus-python 1.0</title>
@@ -36,6 +45,11 @@ for local extensions and $PREFIX/share/nautilus-python/extensions for global ext
     <para>3. For now, some Nautilus class constructors require passing named arguments instead of a standard 
argument list.  This requirement may go away at some point.</para>
     </note>
 
+    <note>
+        <title>Python3</title>
+        <para>As of nautilus-python v1.2.0, nautilus-python can be built to embed python3 instead of 
python2.  It uses the $PYTHON environment variable to determine which library to use.</para>
+    </note>
+
     <xi:include href="nautilus-python-overview-example.xml"/>
     <xi:include href="nautilus-python-overview-methods.xml"/>
 
diff --git a/docs/reference/nautilus-python-property-page-provider.xml 
b/docs/reference/nautilus-python-property-page-provider.xml
index 84879c7..2afa2f9 100644
--- a/docs/reference/nautilus-python-property-page-provider.xml
+++ b/docs/reference/nautilus-python-property-page-provider.xml
@@ -41,11 +41,16 @@
     <title>Nautilus.PropertyPageProvider Example</title>
     <programlisting>
 import hashlib
-import urllib
 
-from gi.repository import Nautilus, GObject, Gtk
+# A way to get unquote working with python 2 and 3
+try:
+    from urllib import unquote
+except ImportError:
+    from urllib.parse import unquote
 
-class ColumnExtension(GObject.GObject, Nautilus.PropertyPageProvider):
+from gi.repository import Nautilus, Gtk, GObject
+
+class MD5SumPropertyPage(GObject.GObject, Nautilus.PropertyPageProvider):
     def __init__(self):
         pass
     
@@ -60,7 +65,7 @@ class ColumnExtension(GObject.GObject, Nautilus.PropertyPageProvider):
         if file.is_directory():
             return
 
-        filename = urllib.unquote(file.get_uri()[7:])
+        filename = unquote(file.get_uri()[7:])
 
         self.property_label = Gtk.Label('MD5Sum')
         self.property_label.show()
@@ -75,13 +80,8 @@ class ColumnExtension(GObject.GObject, Nautilus.PropertyPageProvider):
         self.value_label = Gtk.Label()
         self.hbox.pack_start(self.value_label, False, False, 0)
 
-        md5sum = hashlib.md5()
-        with open(filename,'rb') as f: 
-            for chunk in iter(lambda: f.read(8192), ''): 
-                md5sum.update(chunk)
-        f.close()       
-
-        self.value_label.set_text(md5sum.hexdigest())
+        md5sum = hashlib.md5(filename.encode("utf-8")).hexdigest()
+        self.value_label.set_text(md5sum)
         self.value_label.show()
         
         return Nautilus.PropertyPage(name="NautilusPython::md5_sum",
diff --git a/examples/block-size-column.py b/examples/block-size-column.py
index fda87a9..e481dff 100644
--- a/examples/block-size-column.py
+++ b/examples/block-size-column.py
@@ -1,5 +1,10 @@
 import os
-import urllib
+
+# A way to get unquote working with python 2 and 3
+try:
+    from urllib import unquote
+except ImportError:
+    from urllib.parse import unquote
 
 from gi.repository import GObject, Nautilus
 
@@ -17,6 +22,6 @@ class ColumnExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoPro
         if file.get_uri_scheme() != 'file':
             return
         
-        filename = urllib.unquote(file.get_uri()[7:])
+        filename = unquote(file.get_uri()[7:])
         
         file.add_string_attribute('block_size', str(os.stat(filename).st_blksize))
diff --git a/examples/md5sum-property-page.py b/examples/md5sum-property-page.py
index c098738..42e1df7 100644
--- a/examples/md5sum-property-page.py
+++ b/examples/md5sum-property-page.py
@@ -1,5 +1,10 @@
 import hashlib
-import urllib
+
+# A way to get unquote working with python 2 and 3
+try:
+    from urllib import unquote
+except ImportError:
+    from urllib.parse import unquote
 
 from gi.repository import Nautilus, Gtk, GObject
 
@@ -18,7 +23,7 @@ class MD5SumPropertyPage(GObject.GObject, Nautilus.PropertyPageProvider):
         if file.is_directory():
             return
 
-        filename = urllib.unquote(file.get_uri()[7:])
+        filename = unquote(file.get_uri()[7:])
 
         self.property_label = Gtk.Label('MD5Sum')
         self.property_label.show()
@@ -33,13 +38,8 @@ class MD5SumPropertyPage(GObject.GObject, Nautilus.PropertyPageProvider):
         self.value_label = Gtk.Label()
         self.hbox.pack_start(self.value_label, False, False, 0)
 
-        md5sum = hashlib.md5()
-        with open(filename,'rb') as f: 
-            for chunk in iter(lambda: f.read(8192), ''): 
-                md5sum.update(chunk)
-        f.close()       
-
-        self.value_label.set_text(md5sum.hexdigest())
+        md5sum = hashlib.md5(filename.encode("utf-8")).hexdigest()
+        self.value_label.set_text(md5sum)
         self.value_label.show()
         
         return Nautilus.PropertyPage(name="NautilusPython::md5_sum",
diff --git a/examples/open-terminal.py b/examples/open-terminal.py
index 448bc94..60c761f 100644
--- a/examples/open-terminal.py
+++ b/examples/open-terminal.py
@@ -32,9 +32,9 @@ class OpenTerminalExtension(Nautilus.MenuProvider, GObject.GObject):
         file = files[0]
         if not file.is_directory() or file.get_uri_scheme() != 'file':
             return
-        
+       
         item = Nautilus.MenuItem(name='NautilusPython::openterminal_file_item',
-                                 label='Open Terminal 2' ,
+                                 label='Open Terminal' ,
                                  tip='Open Terminal In %s' % file.get_name())
         item.connect('activate', self.menu_activate_cb, file)
         return item,
diff --git a/examples/update-file-info-async.py b/examples/update-file-info-async.py
index a926a03..332a043 100644
--- a/examples/update-file-info-async.py
+++ b/examples/update-file-info-async.py
@@ -2,13 +2,20 @@ from gi.repository import Nautilus, GObject
 
 class UpdateFileInfoAsync(GObject.GObject, Nautilus.InfoProvider):
     def __init__(self):
+        self.timers = []
         pass
     
     def update_file_info_full(self, provider, handle, closure, file):
-        print "update_file_info_full"
-        gobject.timeout_add_seconds(3, self.update_cb, provider, handle, closure)
+        print("update_file_info_full")
+        self.timers.append(GObject.timeout_add_seconds(3, self.update_cb, provider, handle, closure))
         return Nautilus.OperationResult.IN_PROGRESS
         
     def update_cb(self, provider, handle, closure):
-        print "update_cb"
+        print("update_cb")
         Nautilus.info_provider_update_complete_invoke(closure, provider, handle, 
Nautilus.OperationResult.FAILED)
+
+    def cancel_update(self, provider, handle):
+        print("cancel_update")
+        for t in self.timers:
+            GObject.source_remove(t)
+        self.timers = []
diff --git a/src/Makefile.am b/src/Makefile.am
index 285db08..b5030e1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ INCLUDES =                        \
     $(NAUTILUS_PYTHON_CFLAGS)            \
     -DPYTHON_VERSION=\"$(PYTHON_VERSION)\"        \
     -DPY_LIB_LOC="\"$(PYTHON_LIB_LOC)\""        \
+    -DPY_LIB_NAME="\"$(PYTHON_LIB_NAME)\""        \
     $(PYTHON_INCLUDES)
 
 nautilus_extensiondir=$(NAUTILUS_EXTENSION_DIR)
diff --git a/src/nautilus-python-object.c b/src/nautilus-python-object.c
index 8bc439c..b60088b 100644
--- a/src/nautilus-python-object.c
+++ b/src/nautilus-python-object.c
@@ -41,6 +41,38 @@
 
 static GObjectClass *parent_class;
 
+int __PyString_Check(PyObject *obj) {
+#if PY_MAJOR_VERSION >= 3
+    return PyUnicode_Check(obj);
+#else
+    return PyString_Check(obj);
+#endif
+}
+
+char* __PyString_AsString(PyObject *obj) {
+#if PY_MAJOR_VERSION >= 3
+    return PyUnicode_AsUTF8(obj);
+#else
+    return PyString_AsString(obj);
+#endif
+}
+
+PyObject* __PyString_FromString(const char *c) {
+#if PY_MAJOR_VERSION >= 3
+    return PyUnicode_FromString(c);
+#else
+    return PyString_FromString(c);
+#endif
+}
+
+int __PyInt_Check(PyObject *obj) {
+#if PY_MAJOR_VERSION >= 3
+    return PyLong_Check(obj);
+#else
+    return PyInt_Check(obj);
+#endif
+}
+
 /* These macros assumes the following things:
  *   a METHOD_NAME is defined with is a string
  *   a goto label called beach
@@ -82,7 +114,7 @@ static GObjectClass *parent_class;
 #define HANDLE_LIST(py_ret, type, type_name)                           \
     {                                                                  \
         Py_ssize_t i = 0;                                              \
-        if (!PySequence_Check(py_ret) || PyString_Check(py_ret))       \
+        if (!PySequence_Check(py_ret) || __PyString_Check(py_ret))       \
         {                                                              \
             PyErr_SetString(PyExc_TypeError,                           \
                             METHOD_NAME " must return a sequence");    \
@@ -184,7 +216,7 @@ nautilus_python_object_get_widget (NautilusLocationWidgetProvider *provider,
     CHECK_OBJECT(object);
     CHECK_METHOD_NAME(object->instance);
 
-    py_uri = PyString_FromString(uri);
+    py_uri = __PyString_FromString(uri);
 
     py_ret = PyObject_CallMethod(object->instance, METHOD_PREFIX METHOD_NAME,
                                  "(NN)", py_uri,
@@ -398,14 +430,18 @@ nautilus_python_object_update_file_info (NautilusInfoProvider         *provider,
     
     HANDLE_RETVAL(py_ret);
 
-    if (!PyInt_Check(py_ret)) {
+    if (!__PyInt_Check(py_ret)) {
         PyErr_SetString(PyExc_TypeError,
                         METHOD_NAME " must return None or a int");
         goto beach;
     }
 
+#if PY_MAJOR_VERSION >= 3
+    ret = PyLong_AsLong(py_ret);
+#else
     ret = PyInt_AsLong(py_ret);
-    
+#endif
+
 beach:
     free_pygobject_data(file, NULL);
     Py_XDECREF(py_ret);
@@ -489,7 +525,7 @@ nautilus_python_object_get_type (GTypeModule *module,
         NULL
     };
 
-    debug_enter_args("type=%s", PyString_AsString(PyObject_GetAttrString(type, "__name__")));
+    debug_enter_args("type=%s", __PyString_AsString(PyObject_GetAttrString(type, "__name__")));
     info = g_new0 (GTypeInfo, 1);
     
     info->class_size = sizeof (NautilusPythonObjectClass);
@@ -501,7 +537,7 @@ nautilus_python_object_get_type (GTypeModule *module,
     Py_INCREF(type);
 
     type_name = g_strdup_printf("%s+NautilusPython",
-                                PyString_AsString(PyObject_GetAttrString(type, "__name__")));
+                                __PyString_AsString(PyObject_GetAttrString(type, "__name__")));
         
     gtype = g_type_module_register_type (module, 
                                          G_TYPE_OBJECT,
diff --git a/src/nautilus-python.c b/src/nautilus-python.c
index 6bac4fc..694d555 100644
--- a/src/nautilus-python.c
+++ b/src/nautilus-python.c
@@ -131,7 +131,11 @@ nautilus_python_load_dir (GTypeModule *module,
                 
                 /* sys.path.insert(0, dirname) */
                 sys_path = PySys_GetObject("path");
+#if PY_MAJOR_VERSION >= 3
+                py_path = PyUnicode_FromString(dirname);
+#else
                 py_path = PyString_FromString(dirname);
+#endif
                 PyList_Insert(sys_path, 0, py_path);
                 Py_DECREF(py_path);
             }
@@ -145,13 +149,12 @@ static gboolean
 nautilus_python_init_python (void) {
     PyObject *nautilus;
     GModule *libpython;
-    char *argv[] = { "nautilus", NULL };
 
     if (Py_IsInitialized())
         return TRUE;
 
-    debug("g_module_open " PY_LIB_LOC "/libpython" PYTHON_VERSION "." G_MODULE_SUFFIX ".1.0");
-    libpython = g_module_open(PY_LIB_LOC "/libpython" PYTHON_VERSION "." G_MODULE_SUFFIX ".1.0", 0);
+    debug("g_module_open " PY_LIB_LOC "/lib" PY_LIB_NAME "." G_MODULE_SUFFIX ".1.0");  
+    libpython = g_module_open (PY_LIB_LOC "/lib" PY_LIB_NAME "." G_MODULE_SUFFIX ".1.0", 0);
     if (!libpython)
         g_warning("g_module_open libpython failed: %s", g_module_error());
 
@@ -163,6 +166,11 @@ nautilus_python_init_python (void) {
     }
     
     debug("PySys_SetArgv");
+#if PY_MAJOR_VERSION >= 3
+    wchar_t *argv[] = { L"thunar", NULL };
+#else
+    char *argv[] = { "thunar", NULL };
+#endif
     PySys_SetArgv(1, argv);
     if (PyErr_Occurred()) {
         PyErr_Print();
@@ -170,7 +178,7 @@ nautilus_python_init_python (void) {
     }
     
     debug("Sanitize the python search path");
-    PyRun_SimpleString("import sys; sys.path = filter(None, sys.path)");
+    PyRun_SimpleString("import sys; sys.path = [path for path in sys.path if path]");
     if (PyErr_Occurred()) {
         PyErr_Print();
         return FALSE;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]