Blob Blame History Raw
/*
 * Copyright (C) 2006-2015 Juan Pablo Ugarte.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * Authors:
 *   Juan Pablo Ugarte <juanpablougarte@gmail.com>
 */

#include <config.h>

#include <Python.h>
#include <pygobject.h>

#include <gladeui/glade.h>

#if PY_MAJOR_VERSION >= 3

#define WCHAR(str) L##str

#define PY_STRING(str) WCHAR(str)
#define STRING_FROM_PYSTR(pstr) PyUnicode_AsUTF8 (pstr)

#else

/* include bytesobject.h to map PyBytes_* to PyString_* */
#include <bytesobject.h>

#define PY_STRING(str) str
#define STRING_FROM_PYSTR(pstr) PyBytes_AsString (pstr)

#endif

static void
python_init (void)
{
  if (Py_IsInitialized ())
    return;

  Py_InitializeEx (0);

  /* if argc is 0 an empty string is prepended to sys.path */
  PySys_SetArgvEx (0, NULL, 0);
}

static void
glade_python_init_pygobject_check (gint req_major, gint req_minor, gint req_micro)
{
  PyObject *gi, *gobject;

  /* import gobject */
  pygobject_init (req_major, req_minor, req_micro);
  if (PyErr_Occurred ())
    {
      g_warning ("Error initializing Python interpreter: could not "
                 "import pygobject");

      return;
    }

  gi = PyImport_ImportModule ("gi");
  if (gi == NULL)
    {
      g_warning ("Error initializing Python interpreter: could not "
                 "import gi");

      return;
    }

  gobject = PyImport_ImportModule ("gi.repository.GObject");
  if (gobject == NULL)
    {
      g_warning ("Error initializing Python interpreter: could not "
                 "import gobject");

      return;
    }
}

static gboolean
glade_python_setup ()
{
  GString *command;
  const gchar *module_path;
  const GList *paths;

  Py_SetProgramName (PY_STRING (PACKAGE_NAME));

  /* Initialize the Python interpreter */
  python_init ();

  /* Check and init pygobject */

  PyErr_Clear ();
  glade_python_init_pygobject_check (PYGOBJECT_REQUIRED_MAJOR,
                                     PYGOBJECT_REQUIRED_MINOR,
                                     PYGOBJECT_REQUIRED_MICRO);
  if (PyErr_Occurred ())
    {
      PyObject *ptype, *pvalue, *ptraceback, *pstr;
      char *pvalue_char = "";

      PyErr_Fetch (&ptype, &pvalue, &ptraceback);
      PyErr_NormalizeException (&ptype, &pvalue, &ptraceback);

      if ((pstr = PyObject_Str (pvalue)))
        pvalue_char = STRING_FROM_PYSTR (pstr);

      g_warning ("Unable to load pygobject module >= %d.%d.%d, "
                 "please make sure it is in python's path (sys.path). "
                 "(use PYTHONPATH env variable to specify non default paths)\n%s",
                 PYGOBJECT_REQUIRED_MAJOR, PYGOBJECT_REQUIRED_MINOR,
                 PYGOBJECT_REQUIRED_MICRO, pvalue_char);

      Py_DecRef (ptype);
      Py_DecRef (pvalue);
      Py_DecRef (ptraceback);
      Py_DecRef (pstr);
      PyErr_Clear ();
      Py_Finalize ();

      return TRUE;
    }

  pyg_disable_warning_redirections ();

  /* Generate system path array */
  command = g_string_new ("import sys; sys.path+=[");

  /* GLADE_ENV_MODULE_PATH has priority */
  module_path = g_getenv (GLADE_ENV_MODULE_PATH);
  if (module_path)
    g_string_append_printf (command, "'%s', ", module_path);

  /* Append modules directory */
  g_string_append_printf (command, "'%s'", glade_app_get_modules_dir ());
  
  /* Append extra paths (declared in the Preferences) */
  for (paths = glade_catalog_get_extra_paths (); paths; paths = g_list_next (paths))
    g_string_append_printf (command, ", '%s'", (gchar *) paths->data);

  /* Close python statement */
  g_string_append (command, "];\n");

  /* Make sure we load Gtk 3 */
  g_string_append (command, "import gi; gi.require_version('Gtk', '3.0');\n");

  /* Finally run statement in vm */
  PyRun_SimpleString (command->str);

  g_string_free (command, TRUE);

  return FALSE;
}

void
glade_python_init (const gchar *name)
{
  static gsize init = 0;
  gchar *import_sentence;

  if (g_once_init_enter (&init))
    {
      if (glade_python_setup ())
        return;

      g_once_init_leave (&init, TRUE);
    }

  /* Yeah, we use the catalog name as the library */
  import_sentence = g_strdup_printf ("import %s;", name);

  /* Importing the module will create all the GTypes so that glade can use them at runtime */
  PyRun_SimpleString (import_sentence);
  
  g_free (import_sentence);
}