-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathpythonloader.c
161 lines (134 loc) · 5.43 KB
/
pythonloader.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
* pythonloader.c
* Copyright (C) 2015-2016 Adrian Perez <[email protected]>
* Copyright (C) 2016 Nathan Hoad <[email protected]>
*
* Distributed under terms of the MIT license.
*/
#include <webkit2/webkit-web-extension.h>
#include <pygobject.h>
#include <stdarg.h>
/*
* XXX: Hacky workaround ahead!
*
* PyGObject internally uses _pygi_struct_new_from_g_type() to wrap
* GVariant instances into a gi.Struct, but the function is not in the
* public API. Instead, we temporarily wrap the GVariant into a GValue
* (ugh!), and use pyg_value_as_pyobject(), which in turn calls function
* pygi_value_to_py_structured_type() —also private—, and finally that
* in turn calls _pygi_struct_new_from_g_type() as initially desired.
*/
static PyObject*
g_variant_to_pyobject (GVariant *variant)
{
GValue value = { 0, };
g_value_init (&value, G_TYPE_VARIANT);
g_value_set_variant (&value, variant);
return pyg_value_as_pyobject (&value, FALSE);
}
#define py_auto __attribute__((cleanup(py_object_cleanup)))
static void
py_object_cleanup(void *ptr)
{
PyObject **py_obj_location = ptr;
if (py_obj_location) {
Py_DECREF (*py_obj_location);
*py_obj_location = NULL;
}
}
#define PY_CHECK_ACT(expr, act, err_fmt, ...) \
do { \
if (!(expr)) { \
g_printerr (err_fmt, ##__VA_ARGS__); \
if (PyErr_Occurred ()) { \
g_printerr (": "); \
PyErr_Print (); \
} else { \
g_printerr (" (no error given)"); \
} \
act; \
} \
} while (0)
#define PY_CHECK(expr, err_fmt, ...) \
PY_CHECK_ACT (expr, return, err_fmt, ##__VA_ARGS__)
/* This would be "extension.py" from the source directory. */
static const char *extension_name = "extension";
static gboolean
pygi_require (const gchar *module, ...)
{
PyObject py_auto *gi_module = PyImport_ImportModule ("gi");
PY_CHECK_ACT (gi_module, return FALSE, "Could not import 'gi'");
PyObject py_auto *func = PyObject_GetAttrString (gi_module, "require_version");
PY_CHECK_ACT (func, return FALSE,
"Could not obtain 'gi.require_version'");
PY_CHECK_ACT (PyCallable_Check (func), return FALSE,
"Object 'gi.require_version' is not callable");
gboolean result = TRUE;
va_list arglist;
va_start (arglist, module);
while (module) {
/*
* For each module and version, call: gi.require(module, version)
*/
const gchar *version = va_arg (arglist, const gchar*);
{
PyObject py_auto *args = Py_BuildValue ("(ss)", module, version);
PyObject py_auto *rval = PyObject_CallObject (func, args);
PY_CHECK_ACT (rval, result = FALSE; break,
"Error calling 'gi.require_version(\"%s\", \"%s\")'",
module, version);
}
module = va_arg (arglist, const gchar*);
}
va_end (arglist);
return result;
}
G_MODULE_EXPORT void
webkit_web_extension_initialize_with_user_data (WebKitWebExtension *extension,
GVariant *user_data)
{
Py_Initialize ();
#if PY_VERSION_HEX < 0x03000000
const char *argv[] = { "", NULL };
#else
wchar_t *argv[] = { L"", NULL };
#endif
PySys_SetArgvEx (1, argv, 0);
pygobject_init (-1, -1, -1);
if (PyErr_Occurred ()) {
g_printerr ("Could not initialize PyGObject");
return;
}
pyg_enable_threads ();
PyEval_InitThreads ();
if (!pygi_require ("GLib", "2.0",
"WebKit2WebExtension", "4.0",
NULL))
return;
PyObject py_auto *web_ext_module =
PyImport_ImportModule ("gi.repository.WebKit2WebExtension");
PY_CHECK (web_ext_module,
"Could not import 'gi.repository.WebKit2WebExtension'");
/*
* TODO: Instead of assuming that the Python import path contains the
* directory where the extension is, manually load and compile the
* extension code, then use PyImport_AddModule() to programmatically
* create a new module and PyImport_ExecCodeModule() to import it
* from a bytecode object.
*/
PyObject py_auto *py_filename = PyUnicode_FromString (extension_name);
PyObject py_auto *py_module = PyImport_Import (py_filename);
PY_CHECK (py_module, "Could not import '%s'", extension_name);
PyObject py_auto *py_func = PyObject_GetAttrString (py_module, "initialize");
PY_CHECK (py_func, "Could not obtain '%s.initialize'", extension_name);
PY_CHECK (PyCallable_Check (py_func),
"Object '%s.initialize' is not callable", extension_name);
PyObject py_auto *py_extension = pygobject_new (G_OBJECT (extension));
PyObject py_auto *py_extra_args = g_variant_to_pyobject (user_data);
PY_CHECK (py_extra_args, "Cannot create GLib.Variant");
PyObject py_auto py_auto *func_args = PyTuple_New (2);
PyTuple_SetItem (func_args, 0, py_extension);
PyTuple_SetItem (func_args, 1, py_extra_args);
PyObject py_auto *py_retval = PyObject_CallObject (py_func, func_args);
PY_CHECK (py_retval, "Error calling '%s.initialize'", extension_name);
}