1 /*
2 * peas-plugin-loader-python.c
3 * This file is part of libpeas
4 *
5 * Copyright (C) 2008 - Jesse van den Kieboom
6 * Copyright (C) 2009 - Steve Frécinaux
7 *
8 * libpeas is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * libpeas is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "peas-plugin-loader-python.h"
28 #include "peas-python-internal.h"
29 #include "libpeas/peas-plugin-info-priv.h"
30
31 /* _POSIX_C_SOURCE is defined in Python.h and in limits.h included by
32 * glib-object.h, so we unset it here to avoid a warning. Yep, that's bad.
33 */
34 #undef _POSIX_C_SOURCE
35 #include <pygobject.h>
36
37 typedef struct {
38 PyThreadState *py_thread_state;
39
40 guint n_loaded_plugins;
41
42 guint init_failed : 1;
43 guint must_finalize_python : 1;
44 } PeasPluginLoaderPythonPrivate;
45
46 G_DEFINE_TYPE_WITH_PRIVATE (PeasPluginLoaderPython,
47 peas_plugin_loader_python,
48 PEAS_TYPE_PLUGIN_LOADER)
49
50 #define GET_PRIV(o) \
51 (peas_plugin_loader_python_get_instance_private (o))
52
53 static GQuark quark_extension_type = 0;
54
55 G_MODULE_EXPORT void
peas_register_types(PeasObjectModule * module)56 peas_register_types (PeasObjectModule *module)
57 {
58 peas_object_module_register_extension_type (module,
59 PEAS_TYPE_PLUGIN_LOADER,
60 PEAS_TYPE_PLUGIN_LOADER_PYTHON);
61 }
62
63 static GType
find_python_extension_type(GType exten_type,PyObject * pymodule)64 find_python_extension_type (GType exten_type,
65 PyObject *pymodule)
66 {
67 PyObject *pyexten_type, *pytype;
68 GType the_type = G_TYPE_INVALID;
69
70 pyexten_type = pyg_type_wrapper_new (exten_type);
71
72 pytype = peas_python_internal_call ("find_extension_type",
73 &PyType_Type, "(OO)",
74 pyexten_type, pymodule);
75 Py_DECREF (pyexten_type);
76
77 if (pytype != NULL)
78 {
79 the_type = pyg_type_from_object (pytype);
80 Py_DECREF (pytype);
81
82 g_return_val_if_fail (g_type_is_a (the_type, exten_type),
83 G_TYPE_INVALID);
84 }
85
86 return the_type;
87 }
88
89 static gboolean
peas_plugin_loader_python_provides_extension(PeasPluginLoader * loader,PeasPluginInfo * info,GType exten_type)90 peas_plugin_loader_python_provides_extension (PeasPluginLoader *loader,
91 PeasPluginInfo *info,
92 GType exten_type)
93 {
94 PyObject *pymodule = info->loader_data;
95 GType the_type;
96 PyGILState_STATE state = PyGILState_Ensure ();
97
98 the_type = find_python_extension_type (exten_type, pymodule);
99
100 PyGILState_Release (state);
101 return the_type != G_TYPE_INVALID;
102 }
103
104 static PeasExtension *
peas_plugin_loader_python_create_extension(PeasPluginLoader * loader,PeasPluginInfo * info,GType exten_type,guint n_parameters,GParameter * parameters)105 peas_plugin_loader_python_create_extension (PeasPluginLoader *loader,
106 PeasPluginInfo *info,
107 GType exten_type,
108 guint n_parameters,
109 GParameter *parameters)
110 {
111 PyObject *pymodule = info->loader_data;
112 GType the_type;
113 GObject *object = NULL;
114 PyObject *pyobject;
115 PyObject *pyplinfo;
116 PyGILState_STATE state = PyGILState_Ensure ();
117
118 the_type = find_python_extension_type (exten_type, pymodule);
119 if (the_type == G_TYPE_INVALID)
120 goto out;
121
122 object = g_object_newv (the_type, n_parameters, parameters);
123 if (object == NULL)
124 goto out;
125
126 /* Sink floating references if necessary */
127 if (g_object_is_floating (object))
128 g_object_ref_sink (object);
129
130 /* We have to remember which interface we are instantiating
131 * for the deprecated peas_extension_get_extension_type().
132 */
133 g_object_set_qdata (object, quark_extension_type,
134 GSIZE_TO_POINTER (exten_type));
135
136 pyobject = pygobject_new (object);
137 pyplinfo = pyg_boxed_new (PEAS_TYPE_PLUGIN_INFO, info, TRUE, TRUE);
138
139 /* Set the plugin info as an attribute of the instance */
140 if (PyObject_SetAttrString (pyobject, "plugin_info", pyplinfo) != 0)
141 {
142 g_warning ("Failed to set 'plugin_info' for '%s'",
143 g_type_name (the_type));
144
145 if (PyErr_Occurred ())
146 PyErr_Print ();
147
148 g_clear_object (&object);
149 }
150
151 Py_DECREF (pyplinfo);
152 Py_DECREF (pyobject);
153
154 out:
155
156 PyGILState_Release (state);
157 return object;
158 }
159
160 static gboolean
peas_plugin_loader_python_load(PeasPluginLoader * loader,PeasPluginInfo * info)161 peas_plugin_loader_python_load (PeasPluginLoader *loader,
162 PeasPluginInfo *info)
163 {
164 PeasPluginLoaderPython *pyloader = PEAS_PLUGIN_LOADER_PYTHON (loader);
165 PeasPluginLoaderPythonPrivate *priv = GET_PRIV (pyloader);
166 const gchar *module_dir, *module_name;
167 PyObject *pymodule;
168 PyGILState_STATE state = PyGILState_Ensure ();
169
170 module_dir = peas_plugin_info_get_module_dir (info);
171 module_name = peas_plugin_info_get_module_name (info);
172
173 pymodule = peas_python_internal_call ("load", &PyModule_Type, "(sss)",
174 info->filename,
175 module_dir, module_name);
176
177 if (pymodule != NULL)
178 {
179 info->loader_data = pymodule;
180 priv->n_loaded_plugins += 1;
181 }
182
183 PyGILState_Release (state);
184 return pymodule != NULL;
185 }
186
187 static void
peas_plugin_loader_python_unload(PeasPluginLoader * loader,PeasPluginInfo * info)188 peas_plugin_loader_python_unload (PeasPluginLoader *loader,
189 PeasPluginInfo *info)
190 {
191 PeasPluginLoaderPython *pyloader = PEAS_PLUGIN_LOADER_PYTHON (loader);
192 PeasPluginLoaderPythonPrivate *priv = GET_PRIV (pyloader);
193 PyGILState_STATE state = PyGILState_Ensure ();
194
195 /* We have to use this as a hook as the
196 * loader will not be finalized by applications
197 */
198 if (--priv->n_loaded_plugins == 0)
199 peas_python_internal_call ("all_plugins_unloaded", NULL, NULL);
200
201 Py_CLEAR (info->loader_data);
202 PyGILState_Release (state);
203 }
204
205 static void
peas_plugin_loader_python_garbage_collect(PeasPluginLoader * loader)206 peas_plugin_loader_python_garbage_collect (PeasPluginLoader *loader)
207 {
208 PyGILState_STATE state = PyGILState_Ensure ();
209
210 peas_python_internal_call ("garbage_collect", NULL, NULL);
211
212 PyGILState_Release (state);
213 }
214
215 static gboolean
peas_plugin_loader_python_initialize(PeasPluginLoader * loader)216 peas_plugin_loader_python_initialize (PeasPluginLoader *loader)
217 {
218 PeasPluginLoaderPython *pyloader = PEAS_PLUGIN_LOADER_PYTHON (loader);
219 PeasPluginLoaderPythonPrivate *priv = GET_PRIV (pyloader);
220 PyGILState_STATE state = 0;
221 long hexversion;
222
223 /* We can't support multiple Python interpreter states:
224 * https://bugzilla.gnome.org/show_bug.cgi?id=677091
225 */
226
227 /* Python initialization */
228 if (Py_IsInitialized ())
229 {
230 state = PyGILState_Ensure ();
231 }
232 else
233 {
234 Py_InitializeEx (FALSE);
235 priv->must_finalize_python = TRUE;
236 }
237
238 hexversion = PyLong_AsLong (PySys_GetObject ((char *) "hexversion"));
239
240 #if PY_VERSION_HEX < 0x03000000
241 if (hexversion >= 0x03000000)
242 #else
243 if (hexversion < 0x03000000)
244 #endif
245 {
246 g_critical ("Attempting to mix incompatible Python versions");
247
248 goto python_init_error;
249 }
250
251 /* Initialize PyGObject */
252 pygobject_init (PYGOBJECT_MAJOR_VERSION,
253 PYGOBJECT_MINOR_VERSION,
254 PYGOBJECT_MICRO_VERSION);
255
256 if (PyErr_Occurred ())
257 {
258 g_warning ("Error initializing Python Plugin Loader: "
259 "PyGObject initialization failed");
260
261 goto python_init_error;
262 }
263
264 /* Initialize support for threads */
265 pyg_enable_threads ();
266 PyEval_InitThreads ();
267
268 /* Only redirect warnings when python was not already initialized */
269 if (!priv->must_finalize_python)
270 pyg_disable_warning_redirections ();
271
272 /* Must be done last, finalize() depends on init_failed */
273 if (!peas_python_internal_setup (!priv->must_finalize_python))
274 {
275 /* Already warned */
276 goto python_init_error;
277 }
278
279 if (!priv->must_finalize_python)
280 PyGILState_Release (state);
281 else
282 priv->py_thread_state = PyEval_SaveThread ();
283
284 return TRUE;
285
286 python_init_error:
287
288 if (PyErr_Occurred ())
289 PyErr_Print ();
290
291 g_warning ("Please check the installation of all the Python "
292 "related packages required by libpeas and try again");
293
294 if (!priv->must_finalize_python)
295 PyGILState_Release (state);
296
297 priv->init_failed = TRUE;
298 return FALSE;
299 }
300
301 static void
peas_plugin_loader_python_init(PeasPluginLoaderPython * pyloader)302 peas_plugin_loader_python_init (PeasPluginLoaderPython *pyloader)
303 {
304 }
305
306 static void
peas_plugin_loader_python_finalize(GObject * object)307 peas_plugin_loader_python_finalize (GObject *object)
308 {
309 PeasPluginLoaderPython *pyloader = PEAS_PLUGIN_LOADER_PYTHON (object);
310 PeasPluginLoaderPythonPrivate *priv = GET_PRIV (pyloader);
311 PyGILState_STATE state;
312
313 if (!Py_IsInitialized ())
314 goto out;
315
316 g_warn_if_fail (priv->n_loaded_plugins == 0);
317
318 if (!priv->init_failed)
319 {
320 state = PyGILState_Ensure ();
321 peas_python_internal_shutdown ();
322 PyGILState_Release (state);
323 }
324
325 if (priv->py_thread_state)
326 PyEval_RestoreThread (priv->py_thread_state);
327
328 if (priv->must_finalize_python)
329 {
330 if (!priv->init_failed)
331 PyGILState_Ensure ();
332
333 Py_Finalize ();
334 }
335
336 out:
337
338 G_OBJECT_CLASS (peas_plugin_loader_python_parent_class)->finalize (object);
339 }
340
341 static void
peas_plugin_loader_python_class_init(PeasPluginLoaderPythonClass * klass)342 peas_plugin_loader_python_class_init (PeasPluginLoaderPythonClass *klass)
343 {
344 GObjectClass *object_class = G_OBJECT_CLASS (klass);
345 PeasPluginLoaderClass *loader_class = PEAS_PLUGIN_LOADER_CLASS (klass);
346
347 quark_extension_type = g_quark_from_static_string ("peas-extension-type");
348
349 object_class->finalize = peas_plugin_loader_python_finalize;
350
351 loader_class->initialize = peas_plugin_loader_python_initialize;
352 loader_class->load = peas_plugin_loader_python_load;
353 loader_class->unload = peas_plugin_loader_python_unload;
354 loader_class->create_extension = peas_plugin_loader_python_create_extension;
355 loader_class->provides_extension = peas_plugin_loader_python_provides_extension;
356 loader_class->garbage_collect = peas_plugin_loader_python_garbage_collect;
357 }
358