##// END OF EJS Templates
added support for shared library loading in custom importer by using imp.find_module and imp.load_module...
florianlink -
r83:48da745fc84a
parent child
Show More
@@ -1,788 +1,793
1 /*
1 /*
2 *
2 *
3 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
3 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
4 *
4 *
5 * This library is free software; you can redistribute it and/or
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
8 * version 2.1 of the License, or (at your option) any later version.
9 *
9 *
10 * This library is distributed in the hope that it will be useful,
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
13 * Lesser General Public License for more details.
14 *
14 *
15 * Further, this software is distributed without any warranty that it is
15 * Further, this software is distributed without any warranty that it is
16 * free of the rightful claim of any third person regarding infringement
16 * free of the rightful claim of any third person regarding infringement
17 * or the like. Any license provided herein, whether implied or
17 * or the like. Any license provided herein, whether implied or
18 * otherwise, applies only to this software file. Patent licenses, if
18 * otherwise, applies only to this software file. Patent licenses, if
19 * any, provided herein do not apply to combinations of this program with
19 * any, provided herein do not apply to combinations of this program with
20 * other software, or any other product whatsoever.
20 * other software, or any other product whatsoever.
21 *
21 *
22 * You should have received a copy of the GNU Lesser General Public
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
25 *
26 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
26 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
27 * 28359 Bremen, Germany or:
27 * 28359 Bremen, Germany or:
28 *
28 *
29 * http://www.mevis.de
29 * http://www.mevis.de
30 *
30 *
31 */
31 */
32
32
33 //----------------------------------------------------------------------------------
33 //----------------------------------------------------------------------------------
34 /*!
34 /*!
35 // \file PythonQtImporter.h
35 // \file PythonQtImporter.h
36 // \author Florian Link
36 // \author Florian Link
37 // \author Last changed by $Author: florian $
37 // \author Last changed by $Author: florian $
38 // \date 2006-05
38 // \date 2006-05
39 */
39 */
40 // This module was inspired by the zipimport.c module of the original
40 // This module was inspired by the zipimport.c module of the original
41 // Python distribution. Most of the functions are identical or slightly
41 // Python distribution. Most of the functions are identical or slightly
42 // modified to do all the loading of Python files via an external file interface.
42 // modified to do all the loading of Python files via an external file interface.
43 // In contrast to zipimport.c, this module also writes *.pyc files
43 // In contrast to zipimport.c, this module also writes *.pyc files
44 // automatically if it has write access/is not inside of a zip file.
44 // automatically if it has write access/is not inside of a zip file.
45 //----------------------------------------------------------------------------------
45 //----------------------------------------------------------------------------------
46
46
47 #include "PythonQtImporter.h"
47 #include "PythonQtImporter.h"
48 #include "PythonQtImportFileInterface.h"
48 #include "PythonQtImportFileInterface.h"
49 #include "PythonQt.h"
49 #include "PythonQt.h"
50 #include "PythonQtConversion.h"
50 #include <QFile>
51 #include <QFile>
51 #include <QFileInfo>
52 #include <QFileInfo>
52
53
53 #define IS_SOURCE 0x0
54 #define IS_SOURCE 0x0
54 #define IS_BYTECODE 0x1
55 #define IS_BYTECODE 0x1
55 #define IS_PACKAGE 0x2
56 #define IS_PACKAGE 0x2
56
57
57 struct st_mlab_searchorder {
58 struct st_mlab_searchorder {
58 char suffix[14];
59 char suffix[14];
59 int type;
60 int type;
60 };
61 };
61
62
62 /* mlab_searchorder defines how we search for a module in the Zip
63 /* mlab_searchorder defines how we search for a module in the Zip
63 archive: we first search for a package __init__, then for
64 archive: we first search for a package __init__, then for
64 non-package .pyc, .pyo and .py entries. The .pyc and .pyo entries
65 non-package .pyc, .pyo and .py entries. The .pyc and .pyo entries
65 are swapped by initmlabimport() if we run in optimized mode. Also,
66 are swapped by initmlabimport() if we run in optimized mode. Also,
66 '/' is replaced by SEP there. */
67 '/' is replaced by SEP there. */
67 struct st_mlab_searchorder mlab_searchorder[] = {
68 struct st_mlab_searchorder mlab_searchorder[] = {
68 {"/__init__.pyc", IS_PACKAGE | IS_BYTECODE},
69 {"/__init__.pyc", IS_PACKAGE | IS_BYTECODE},
69 {"/__init__.pyo", IS_PACKAGE | IS_BYTECODE},
70 {"/__init__.pyo", IS_PACKAGE | IS_BYTECODE},
70 {"/__init__.py", IS_PACKAGE | IS_SOURCE},
71 {"/__init__.py", IS_PACKAGE | IS_SOURCE},
71 {".pyc", IS_BYTECODE},
72 {".pyc", IS_BYTECODE},
72 {".pyo", IS_BYTECODE},
73 {".pyo", IS_BYTECODE},
73 {".py", IS_SOURCE},
74 {".py", IS_SOURCE},
74 {"", 0}
75 {"", 0}
75 };
76 };
76
77
77 extern PyTypeObject PythonQtImporter_Type;
78 extern PyTypeObject PythonQtImporter_Type;
78 PyObject *PythonQtImportError;
79 PyObject *PythonQtImportError;
79
80
80 QString PythonQtImport::getSubName(const QString& str)
81 QString PythonQtImport::getSubName(const QString& str)
81 {
82 {
82 int idx = str.lastIndexOf('.');
83 int idx = str.lastIndexOf('.');
83 if (idx!=-1) {
84 if (idx!=-1) {
84 return str.mid(idx+1);
85 return str.mid(idx+1);
85 } else {
86 } else {
86 return str;
87 return str;
87 }
88 }
88 }
89 }
89
90
90 PythonQtImport::module_info PythonQtImport::getModuleInfo(PythonQtImporter* self, const QString& fullname)
91 PythonQtImport::ModuleInfo PythonQtImport::getModuleInfo(PythonQtImporter* self, const QString& fullname)
91 {
92 {
93 ModuleInfo info;
92 QString subname;
94 QString subname;
93 struct st_mlab_searchorder *zso;
95 struct st_mlab_searchorder *zso;
94
96
95 subname = getSubName(fullname);
97 subname = getSubName(fullname);
96 QString path = *self->_path + "/" + subname;
98 QString path = *self->_path + "/" + subname;
97
99
98 QString test;
100 QString test;
99 for (zso = mlab_searchorder; *zso->suffix; zso++) {
101 for (zso = mlab_searchorder; *zso->suffix; zso++) {
100 test = path + zso->suffix;
102 test = path + zso->suffix;
101 if (PythonQt::importInterface()->exists(test)) {
103 if (PythonQt::importInterface()->exists(test)) {
102 if (zso->type & IS_PACKAGE)
104 info.fullPath = test;
103 return MI_PACKAGE;
105 info.moduleName = subname;
104 else
106 info.type = (zso->type & IS_PACKAGE)?MI_PACKAGE:MI_MODULE;
105 return MI_MODULE;
107 return info;
106 }
108 }
107 }
109 }
108 if (PythonQt::importInterface()->exists(path+".so")) {
110 // test if it is a shared library
109 return MI_SHAREDLIBRARY;
111 foreach(const QString& suffix, PythonQt::priv()->sharedLibrarySuffixes()) {
112 test = path+suffix;
113 if (PythonQt::importInterface()->exists(test)) {
114 info.fullPath = test;
115 info.moduleName = subname;
116 info.type = MI_SHAREDLIBRARY;
117 }
110 }
118 }
111 return MI_NOT_FOUND;
119 return info;
112 }
120 }
113
121
114
122
115 /* PythonQtImporter.__init__
123 /* PythonQtImporter.__init__
116 Just store the path argument (or reject if it is in the ignorePaths list
124 Just store the path argument (or reject if it is in the ignorePaths list
117 */
125 */
118 int PythonQtImporter_init(PythonQtImporter *self, PyObject *args, PyObject * /*kwds*/)
126 int PythonQtImporter_init(PythonQtImporter *self, PyObject *args, PyObject * /*kwds*/)
119 {
127 {
120 self->_path = NULL;
128 self->_path = NULL;
121
129
122 const char* cpath;
130 const char* cpath;
123 if (!PyArg_ParseTuple(args, "s",
131 if (!PyArg_ParseTuple(args, "s",
124 &cpath))
132 &cpath))
125 return -1;
133 return -1;
126
134
127 QString path(cpath);
135 QString path(cpath);
128 if (PythonQt::importInterface()->exists(path)) {
136 if (PythonQt::importInterface()->exists(path)) {
129 const QStringList& ignorePaths = PythonQt::self()->getImporterIgnorePaths();
137 const QStringList& ignorePaths = PythonQt::self()->getImporterIgnorePaths();
130 foreach(QString ignorePath, ignorePaths) {
138 foreach(QString ignorePath, ignorePaths) {
131 if (path.startsWith(ignorePath)) {
139 if (path.startsWith(ignorePath)) {
132 PyErr_SetString(PythonQtImportError,
140 PyErr_SetString(PythonQtImportError,
133 "path ignored");
141 "path ignored");
134 return -1;
142 return -1;
135 }
143 }
136 }
144 }
137
145
138 self->_path = new QString(path);
146 self->_path = new QString(path);
139 return 0;
147 return 0;
140 } else {
148 } else {
141 PyErr_SetString(PythonQtImportError,
149 PyErr_SetString(PythonQtImportError,
142 "path does not exist error");
150 "path does not exist error");
143 return -1;
151 return -1;
144 }
152 }
145 }
153 }
146
154
147 void
155 void
148 PythonQtImporter_dealloc(PythonQtImporter *self)
156 PythonQtImporter_dealloc(PythonQtImporter *self)
149 {
157 {
150 // free the stored path
158 // free the stored path
151 if (self->_path) delete self->_path;
159 if (self->_path) delete self->_path;
152 // free ourself
160 // free ourself
153 self->ob_type->tp_free((PyObject *)self);
161 self->ob_type->tp_free((PyObject *)self);
154 }
162 }
155
163
156
164
157 /* Check whether we can satisfy the import of the module named by
165 /* Check whether we can satisfy the import of the module named by
158 'fullname'. Return self if we can, None if we can't. */
166 'fullname'. Return self if we can, None if we can't. */
159 PyObject *
167 PyObject *
160 PythonQtImporter_find_module(PyObject *obj, PyObject *args)
168 PythonQtImporter_find_module(PyObject *obj, PyObject *args)
161 {
169 {
162 PythonQtImporter *self = (PythonQtImporter *)obj;
170 PythonQtImporter *self = (PythonQtImporter *)obj;
163 PyObject *path = NULL;
171 PyObject *path = NULL;
164 char *fullname;
172 char *fullname;
165
173
166 if (!PyArg_ParseTuple(args, "s|O:PythonQtImporter.find_module",
174 if (!PyArg_ParseTuple(args, "s|O:PythonQtImporter.find_module",
167 &fullname, &path))
175 &fullname, &path))
168 return NULL;
176 return NULL;
169
177
170 qDebug() << "looking for " << fullname << " at " << *self->_path;
178 //qDebug() << "looking for " << fullname << " at " << *self->_path;
171
179
172 // mlabDebugConst("MLABPython", "FindModule " << fullname << " in " << *self->_path);
180 PythonQtImport::ModuleInfo info = PythonQtImport::getModuleInfo(self, fullname);
173
181 if (info.type != PythonQtImport::MI_NOT_FOUND) {
174 PythonQtImport::module_info info = PythonQtImport::getModuleInfo(self, fullname);
175 if (info == PythonQtImport::MI_MODULE || info == PythonQtImport::MI_PACKAGE ||
176 info== PythonQtImport::MI_SHAREDLIBRARY) {
177 Py_INCREF(self);
182 Py_INCREF(self);
178 return (PyObject *)self;
183 return (PyObject *)self;
179 } else {
184 } else {
180 Py_INCREF(Py_None);
185 Py_INCREF(Py_None);
181 return Py_None;
186 return Py_None;
182 }
187 }
183 }
188 }
184
189
185 /* Load and return the module named by 'fullname'. */
190 /* Load and return the module named by 'fullname'. */
186 PyObject *
191 PyObject *
187 PythonQtImporter_load_module(PyObject *obj, PyObject *args)
192 PythonQtImporter_load_module(PyObject *obj, PyObject *args)
188 {
193 {
189 PythonQtImporter *self = (PythonQtImporter *)obj;
194 PythonQtImporter *self = (PythonQtImporter *)obj;
190 PyObject *code, *mod, *dict;
195 PyObject *code = NULL, *mod = NULL, *dict = NULL;
191 char *fullname;
196 char *fullname;
192 QString modpath;
193 int ispackage;
194
197
195 if (!PyArg_ParseTuple(args, "s:PythonQtImporter.load_module",
198 if (!PyArg_ParseTuple(args, "s:PythonQtImporter.load_module",
196 &fullname))
199 &fullname))
197 return NULL;
200 return NULL;
198
201
199 code = PythonQtImport::getModuleCode(self, fullname, &ispackage, modpath);
202 PythonQtImport::ModuleInfo info = PythonQtImport::getModuleInfo(self, fullname);
203 if (info.type == PythonQtImport::MI_NOT_FOUND) {
204 return NULL;
205 }
206
207 if (info.type == PythonQtImport::MI_PACKAGE || info.type == PythonQtImport::MI_MODULE) {
208 QString fullPath;
209 code = PythonQtImport::getModuleCode(self, fullname, fullPath);
200 if (code == NULL) {
210 if (code == NULL) {
201 return NULL;
211 return NULL;
202 }
212 }
203
213
204 mod = PyImport_AddModule(fullname);
214 mod = PyImport_AddModule(fullname);
205 if (mod == NULL) {
215 if (mod == NULL) {
206 Py_DECREF(code);
216 Py_DECREF(code);
207 return NULL;
217 return NULL;
208 }
218 }
209 dict = PyModule_GetDict(mod);
219 dict = PyModule_GetDict(mod);
210
220
211 if (PyDict_SetItemString(dict, "__loader__", (PyObject *)self) != 0) {
221 if (PyDict_SetItemString(dict, "__loader__", (PyObject *)self) != 0) {
212 Py_DECREF(code);
222 Py_DECREF(code);
213 Py_DECREF(mod);
223 Py_DECREF(mod);
214 return NULL;
224 return NULL;
215 }
225 }
216
226
217 if (ispackage) {
227 if (info.type == PythonQtImport::MI_PACKAGE) {
218 PyObject *pkgpath, *fullpath;
228 PyObject *pkgpath, *fullpath;
219 QString subname = PythonQtImport::getSubName(fullname);
229 QString subname = info.moduleName;
220 int err;
230 int err;
221
231
222 fullpath = PyString_FromFormat("%s%c%s",
232 fullpath = PyString_FromFormat("%s%c%s",
223 self->_path->toLatin1().constData(),
233 self->_path->toLatin1().constData(),
224 SEP,
234 SEP,
225 subname.toLatin1().constData());
235 subname.toLatin1().constData());
226 if (fullpath == NULL) {
236 if (fullpath == NULL) {
227 Py_DECREF(code);
237 Py_DECREF(code);
228 Py_DECREF(mod);
238 Py_DECREF(mod);
229 return NULL;
239 return NULL;
230 }
240 }
231
241
232 pkgpath = Py_BuildValue("[O]", fullpath);
242 pkgpath = Py_BuildValue("[O]", fullpath);
233 Py_DECREF(fullpath);
243 Py_DECREF(fullpath);
234 if (pkgpath == NULL) {
244 if (pkgpath == NULL) {
235 Py_DECREF(code);
245 Py_DECREF(code);
236 Py_DECREF(mod);
246 Py_DECREF(mod);
237 return NULL;
247 return NULL;
238 }
248 }
239 err = PyDict_SetItemString(dict, "__path__", pkgpath);
249 err = PyDict_SetItemString(dict, "__path__", pkgpath);
240 Py_DECREF(pkgpath);
250 Py_DECREF(pkgpath);
241 if (err != 0) {
251 if (err != 0) {
242 Py_DECREF(code);
252 Py_DECREF(code);
243 Py_DECREF(mod);
253 Py_DECREF(mod);
244 return NULL;
254 return NULL;
245 }
255 }
246 }
256 }
247 mod = PyImport_ExecCodeModuleEx(fullname, code, (char*)modpath.toLatin1().data());
257 mod = PyImport_ExecCodeModuleEx(fullname, code, fullPath.toLatin1().data());
248 Py_DECREF(code);
258 Py_DECREF(code);
249 if (Py_VerboseFlag) {
259 if (Py_VerboseFlag) {
250 PySys_WriteStderr("import %s # loaded from %s\n",
260 PySys_WriteStderr("import %s # loaded from %s\n",
251 fullname, modpath.toLatin1().constData());
261 fullname, fullPath.toLatin1().constData());
262 }
263 } else {
264 PythonQtObjectPtr imp;
265 imp.setNewRef(PyImport_ImportModule("imp"));
266
267 // Create a PyList with the current path as its single element,
268 // which is required for find_module (it won't accept a tuple...)
269 PythonQtObjectPtr pathList;
270 pathList.setNewRef(PythonQtConv::QStringListToPyList(QStringList() << *self->_path));
271
272 QVariantList args;
273 // Pass the module name without the package prefix
274 args.append(info.moduleName);
275 // And the path where we know that the shared library is
276 args.append(QVariant::fromValue(pathList));
277 QVariant result = imp.call("find_module", args);
278 if (result.isValid()) {
279 // This will return a tuple with (file, pathname, description)
280 QVariantList list = result.toList();
281 if (list.count()==3) {
282 // We prepend the full module name (including package prefix)
283 list.prepend(fullname);
284 // And call "load_module" with (fullname, file, pathname, description)
285 PythonQtObjectPtr module = imp.call("load_module", list);
286 mod = module.object();
287 if (mod) {
288 Py_INCREF(mod);
289 }
290
291 // Finally, we need to close the file again, which find_module opened for us
292 PythonQtObjectPtr file = list.at(1);
293 file.call("close");
294 }
295 }
252 }
296 }
253 return mod;
297 return mod;
254 }
298 }
255
299
256
300
257 PyObject *
301 PyObject *
258 PythonQtImporter_get_data(PyObject* /*obj*/, PyObject* /*args*/)
302 PythonQtImporter_get_data(PyObject* /*obj*/, PyObject* /*args*/)
259 {
303 {
260 // EXTRA, NOT YET IMPLEMENTED
304 // EXTRA, NOT YET IMPLEMENTED
261 return NULL;
305 return NULL;
262 }
306 }
263
307
264 PyObject *
308 PyObject *
265 PythonQtImporter_get_code(PyObject *obj, PyObject *args)
309 PythonQtImporter_get_code(PyObject *obj, PyObject *args)
266 {
310 {
267 PythonQtImporter *self = (PythonQtImporter *)obj;
311 PythonQtImporter *self = (PythonQtImporter *)obj;
268 char *fullname;
312 char *fullname;
269
313
270 if (!PyArg_ParseTuple(args, "s:PythonQtImporter.get_code", &fullname))
314 if (!PyArg_ParseTuple(args, "s:PythonQtImporter.get_code", &fullname))
271 return NULL;
315 return NULL;
272
316
273 QString notused;
317 QString notused;
274 return PythonQtImport::getModuleCode(self, fullname, NULL, notused);
318 return PythonQtImport::getModuleCode(self, fullname, notused);
275 }
319 }
276
320
277 PyObject *
321 PyObject *
278 PythonQtImporter_get_source(PyObject * /*obj*/, PyObject * /*args*/)
322 PythonQtImporter_get_source(PyObject * /*obj*/, PyObject * /*args*/)
279 {
323 {
280 // EXTRA, NOT YET IMPLEMENTED
324 // EXTRA, NOT YET IMPLEMENTED
281 /*
282 PythonQtImporter *self = (PythonQtImporter *)obj;
283 PyObject *toc_entry;
284 char *fullname, *subname, path[MAXPATHLEN+1];
285 int len;
286 enum module_info mi;
287
288 if (!PyArg_ParseTuple(args, "s:PythonQtImporter.get_source", &fullname))
289 return NULL;
290
291 mi = get_module_info(self, fullname);
292 if (mi == MI_ERROR)
293 return NULL;
294 if (mi == MI_NOT_FOUND) {
295 PyErr_Format(PythonQtImportError, "can't find module '%.200s'",
296 fullname);
297 return NULL;
298 }
299 subname = get_subname(fullname);
300
301 len = make_filename(PyString_AsString(self->prefix), subname, path);
302 if (len < 0)
303 return NULL;
304
305 if (mi == MI_PACKAGE) {
306 path[len] = SEP;
307 strcpy(path + len + 1, "__init__.py");
308 }
309 else
310 strcpy(path + len, ".py");
311
312 toc_entry = PyDict_GetItemString(self->files, path);
313 if (toc_entry != NULL)
314 return get_data(PyString_AsString(self->archive), toc_entry);
315
316 Py_INCREF(Py_None);
317 return Py_None;
318 */
319 return NULL;
325 return NULL;
320 }
326 }
321
327
322 PyDoc_STRVAR(doc_find_module,
328 PyDoc_STRVAR(doc_find_module,
323 "find_module(fullname, path=None) -> self or None.\n\
329 "find_module(fullname, path=None) -> self or None.\n\
324 \n\
330 \n\
325 Search for a module specified by 'fullname'. 'fullname' must be the\n\
331 Search for a module specified by 'fullname'. 'fullname' must be the\n\
326 fully qualified (dotted) module name. It returns the PythonQtImporter\n\
332 fully qualified (dotted) module name. It returns the PythonQtImporter\n\
327 instance itself if the module was found, or None if it wasn't.\n\
333 instance itself if the module was found, or None if it wasn't.\n\
328 The optional 'path' argument is ignored -- it's there for compatibility\n\
334 The optional 'path' argument is ignored -- it's there for compatibility\n\
329 with the importer protocol.");
335 with the importer protocol.");
330
336
331 PyDoc_STRVAR(doc_load_module,
337 PyDoc_STRVAR(doc_load_module,
332 "load_module(fullname) -> module.\n\
338 "load_module(fullname) -> module.\n\
333 \n\
339 \n\
334 Load the module specified by 'fullname'. 'fullname' must be the\n\
340 Load the module specified by 'fullname'. 'fullname' must be the\n\
335 fully qualified (dotted) module name. It returns the imported\n\
341 fully qualified (dotted) module name. It returns the imported\n\
336 module, or raises PythonQtImportError if it wasn't found.");
342 module, or raises PythonQtImportError if it wasn't found.");
337
343
338 PyDoc_STRVAR(doc_get_data,
344 PyDoc_STRVAR(doc_get_data,
339 "get_data(pathname) -> string with file data.\n\
345 "get_data(pathname) -> string with file data.\n\
340 \n\
346 \n\
341 Return the data associated with 'pathname'. Raise IOError if\n\
347 Return the data associated with 'pathname'. Raise IOError if\n\
342 the file wasn't found.");
348 the file wasn't found.");
343
349
344 PyDoc_STRVAR(doc_get_code,
350 PyDoc_STRVAR(doc_get_code,
345 "get_code(fullname) -> code object.\n\
351 "get_code(fullname) -> code object.\n\
346 \n\
352 \n\
347 Return the code object for the specified module. Raise PythonQtImportError\n\
353 Return the code object for the specified module. Raise PythonQtImportError\n\
348 is the module couldn't be found.");
354 is the module couldn't be found.");
349
355
350 PyDoc_STRVAR(doc_get_source,
356 PyDoc_STRVAR(doc_get_source,
351 "get_source(fullname) -> source string.\n\
357 "get_source(fullname) -> source string.\n\
352 \n\
358 \n\
353 Return the source code for the specified module. Raise PythonQtImportError\n\
359 Return the source code for the specified module. Raise PythonQtImportError\n\
354 is the module couldn't be found, return None if the archive does\n\
360 is the module couldn't be found, return None if the archive does\n\
355 contain the module, but has no source for it.");
361 contain the module, but has no source for it.");
356
362
357 PyMethodDef PythonQtImporter_methods[] = {
363 PyMethodDef PythonQtImporter_methods[] = {
358 {"find_module", PythonQtImporter_find_module, METH_VARARGS,
364 {"find_module", PythonQtImporter_find_module, METH_VARARGS,
359 doc_find_module},
365 doc_find_module},
360 {"load_module", PythonQtImporter_load_module, METH_VARARGS,
366 {"load_module", PythonQtImporter_load_module, METH_VARARGS,
361 doc_load_module},
367 doc_load_module},
362 {"get_data", PythonQtImporter_get_data, METH_VARARGS,
368 {"get_data", PythonQtImporter_get_data, METH_VARARGS,
363 doc_get_data},
369 doc_get_data},
364 {"get_code", PythonQtImporter_get_code, METH_VARARGS,
370 {"get_code", PythonQtImporter_get_code, METH_VARARGS,
365 doc_get_code},
371 doc_get_code},
366 {"get_source", PythonQtImporter_get_source, METH_VARARGS,
372 {"get_source", PythonQtImporter_get_source, METH_VARARGS,
367 doc_get_source},
373 doc_get_source},
368 {NULL, NULL, 0 , NULL} /* sentinel */
374 {NULL, NULL, 0 , NULL} /* sentinel */
369 };
375 };
370
376
371
377
372 PyDoc_STRVAR(PythonQtImporter_doc,
378 PyDoc_STRVAR(PythonQtImporter_doc,
373 "PythonQtImporter(path) -> PythonQtImporter object\n\
379 "PythonQtImporter(path) -> PythonQtImporter object\n\
374 \n\
380 \n\
375 Create a new PythonQtImporter instance. 'path' must be a valid path on disk/or inside of a zip file known to MeVisLab\n\
381 Create a new PythonQtImporter instance. 'path' must be a valid path on disk/or inside of a zip file known to MeVisLab\n\
376 . Every path is accepted.");
382 . Every path is accepted.");
377
383
378 #define DEFERRED_ADDRESS(ADDR) 0
384 #define DEFERRED_ADDRESS(ADDR) 0
379
385
380 PyTypeObject PythonQtImporter_Type = {
386 PyTypeObject PythonQtImporter_Type = {
381 PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type))
387 PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type))
382 0,
388 0,
383 "PythonQtImport.PythonQtImporter",
389 "PythonQtImport.PythonQtImporter",
384 sizeof(PythonQtImporter),
390 sizeof(PythonQtImporter),
385 0, /* tp_itemsize */
391 0, /* tp_itemsize */
386 (destructor)PythonQtImporter_dealloc, /* tp_dealloc */
392 (destructor)PythonQtImporter_dealloc, /* tp_dealloc */
387 0, /* tp_print */
393 0, /* tp_print */
388 0, /* tp_getattr */
394 0, /* tp_getattr */
389 0, /* tp_setattr */
395 0, /* tp_setattr */
390 0, /* tp_compare */
396 0, /* tp_compare */
391 0, /* tp_repr */
397 0, /* tp_repr */
392 0, /* tp_as_number */
398 0, /* tp_as_number */
393 0, /* tp_as_sequence */
399 0, /* tp_as_sequence */
394 0, /* tp_as_mapping */
400 0, /* tp_as_mapping */
395 0, /* tp_hash */
401 0, /* tp_hash */
396 0, /* tp_call */
402 0, /* tp_call */
397 0, /* tp_str */
403 0, /* tp_str */
398 PyObject_GenericGetAttr, /* tp_getattro */
404 PyObject_GenericGetAttr, /* tp_getattro */
399 0, /* tp_setattro */
405 0, /* tp_setattro */
400 0, /* tp_as_buffer */
406 0, /* tp_as_buffer */
401 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , /* tp_flags */
407 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , /* tp_flags */
402 PythonQtImporter_doc, /* tp_doc */
408 PythonQtImporter_doc, /* tp_doc */
403 0, /* tp_traverse */
409 0, /* tp_traverse */
404 0, /* tp_clear */
410 0, /* tp_clear */
405 0, /* tp_richcompare */
411 0, /* tp_richcompare */
406 0, /* tp_weaklistoffset */
412 0, /* tp_weaklistoffset */
407 0, /* tp_iter */
413 0, /* tp_iter */
408 0, /* tp_iternext */
414 0, /* tp_iternext */
409 PythonQtImporter_methods, /* tp_methods */
415 PythonQtImporter_methods, /* tp_methods */
410 0, /* tp_members */
416 0, /* tp_members */
411 0, /* tp_getset */
417 0, /* tp_getset */
412 0, /* tp_base */
418 0, /* tp_base */
413 0, /* tp_dict */
419 0, /* tp_dict */
414 0, /* tp_descr_get */
420 0, /* tp_descr_get */
415 0, /* tp_descr_set */
421 0, /* tp_descr_set */
416 0, /* tp_dictoffset */
422 0, /* tp_dictoffset */
417 (initproc)PythonQtImporter_init, /* tp_init */
423 (initproc)PythonQtImporter_init, /* tp_init */
418 PyType_GenericAlloc, /* tp_alloc */
424 PyType_GenericAlloc, /* tp_alloc */
419 PyType_GenericNew, /* tp_new */
425 PyType_GenericNew, /* tp_new */
420 PyObject_Del, /* tp_free */
426 PyObject_Del, /* tp_free */
421 };
427 };
422
428
423
429
424 /* Given a buffer, return the long that is represented by the first
430 /* Given a buffer, return the long that is represented by the first
425 4 bytes, encoded as little endian. This partially reimplements
431 4 bytes, encoded as little endian. This partially reimplements
426 marshal.c:r_long() */
432 marshal.c:r_long() */
427 long
433 long
428 PythonQtImport::getLong(unsigned char *buf)
434 PythonQtImport::getLong(unsigned char *buf)
429 {
435 {
430 long x;
436 long x;
431 x = buf[0];
437 x = buf[0];
432 x |= (long)buf[1] << 8;
438 x |= (long)buf[1] << 8;
433 x |= (long)buf[2] << 16;
439 x |= (long)buf[2] << 16;
434 x |= (long)buf[3] << 24;
440 x |= (long)buf[3] << 24;
435 #if SIZEOF_LONG > 4
441 #if SIZEOF_LONG > 4
436 /* Sign extension for 64-bit machines */
442 /* Sign extension for 64-bit machines */
437 x |= -(x & 0x80000000L);
443 x |= -(x & 0x80000000L);
438 #endif
444 #endif
439 return x;
445 return x;
440 }
446 }
441
447
442 FILE *
448 FILE *
443 open_exclusive(const QString& filename)
449 open_exclusive(const QString& filename)
444 {
450 {
445 #if defined(O_EXCL)&&defined(O_CREAT)&&defined(O_WRONLY)&&defined(O_TRUNC)
451 #if defined(O_EXCL)&&defined(O_CREAT)&&defined(O_WRONLY)&&defined(O_TRUNC)
446 /* Use O_EXCL to avoid a race condition when another process tries to
452 /* Use O_EXCL to avoid a race condition when another process tries to
447 write the same file. When that happens, our open() call fails,
453 write the same file. When that happens, our open() call fails,
448 which is just fine (since it's only a cache).
454 which is just fine (since it's only a cache).
449 XXX If the file exists and is writable but the directory is not
455 XXX If the file exists and is writable but the directory is not
450 writable, the file will never be written. Oh well.
456 writable, the file will never be written. Oh well.
451 */
457 */
452 QFile::remove(filename);
458 QFile::remove(filename);
453
459
454 int fd;
460 int fd;
455 int flags = O_EXCL|O_CREAT|O_WRONLY|O_TRUNC;
461 int flags = O_EXCL|O_CREAT|O_WRONLY|O_TRUNC;
456 #ifdef O_BINARY
462 #ifdef O_BINARY
457 flags |= O_BINARY; /* necessary for Windows */
463 flags |= O_BINARY; /* necessary for Windows */
458 #endif
464 #endif
459 #ifdef WIN32
465 #ifdef WIN32
460 fd = _wopen(filename.ucs2(), flags, 0666);
466 fd = _wopen(filename.ucs2(), flags, 0666);
461 #else
467 #else
462 fd = open(filename.local8Bit(), flags, 0666);
468 fd = open(filename.local8Bit(), flags, 0666);
463 #endif
469 #endif
464 if (fd < 0)
470 if (fd < 0)
465 return NULL;
471 return NULL;
466 return fdopen(fd, "wb");
472 return fdopen(fd, "wb");
467 #else
473 #else
468 /* Best we can do -- on Windows this can't happen anyway */
474 /* Best we can do -- on Windows this can't happen anyway */
469 return fopen(filename.toLocal8Bit().constData(), "wb");
475 return fopen(filename.toLocal8Bit().constData(), "wb");
470 #endif
476 #endif
471 }
477 }
472
478
473
479
474 void PythonQtImport::writeCompiledModule(PyCodeObject *co, const QString& filename, long mtime)
480 void PythonQtImport::writeCompiledModule(PyCodeObject *co, const QString& filename, long mtime)
475 {
481 {
476 FILE *fp;
482 FILE *fp;
477 // we do not want to write Qt resources to disk, do we?
483 // we do not want to write Qt resources to disk, do we?
478 if (filename.startsWith(":")) {
484 if (filename.startsWith(":")) {
479 return;
485 return;
480 }
486 }
481 fp = open_exclusive(filename);
487 fp = open_exclusive(filename);
482 if (fp == NULL) {
488 if (fp == NULL) {
483 if (Py_VerboseFlag)
489 if (Py_VerboseFlag)
484 PySys_WriteStderr(
490 PySys_WriteStderr(
485 "# can't create %s\n", filename.toLatin1().constData());
491 "# can't create %s\n", filename.toLatin1().constData());
486 return;
492 return;
487 }
493 }
488 #if PY_VERSION_HEX < 0x02040000
494 #if PY_VERSION_HEX < 0x02040000
489 PyMarshal_WriteLongToFile(PyImport_GetMagicNumber(), fp);
495 PyMarshal_WriteLongToFile(PyImport_GetMagicNumber(), fp);
490 #else
496 #else
491 PyMarshal_WriteLongToFile(PyImport_GetMagicNumber(), fp, Py_MARSHAL_VERSION);
497 PyMarshal_WriteLongToFile(PyImport_GetMagicNumber(), fp, Py_MARSHAL_VERSION);
492 #endif
498 #endif
493 /* First write a 0 for mtime */
499 /* First write a 0 for mtime */
494 #if PY_VERSION_HEX < 0x02040000
500 #if PY_VERSION_HEX < 0x02040000
495 PyMarshal_WriteLongToFile(0L, fp);
501 PyMarshal_WriteLongToFile(0L, fp);
496 #else
502 #else
497 PyMarshal_WriteLongToFile(0L, fp, Py_MARSHAL_VERSION);
503 PyMarshal_WriteLongToFile(0L, fp, Py_MARSHAL_VERSION);
498 #endif
504 #endif
499 #if PY_VERSION_HEX < 0x02040000
505 #if PY_VERSION_HEX < 0x02040000
500 PyMarshal_WriteObjectToFile((PyObject *)co, fp);
506 PyMarshal_WriteObjectToFile((PyObject *)co, fp);
501 #else
507 #else
502 PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION);
508 PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION);
503 #endif
509 #endif
504 if (ferror(fp)) {
510 if (ferror(fp)) {
505 if (Py_VerboseFlag)
511 if (Py_VerboseFlag)
506 PySys_WriteStderr("# can't write %s\n", filename.toLatin1().constData());
512 PySys_WriteStderr("# can't write %s\n", filename.toLatin1().constData());
507 /* Don't keep partial file */
513 /* Don't keep partial file */
508 fclose(fp);
514 fclose(fp);
509 QFile::remove(filename);
515 QFile::remove(filename);
510 return;
516 return;
511 }
517 }
512 /* Now write the true mtime */
518 /* Now write the true mtime */
513 fseek(fp, 4L, 0);
519 fseek(fp, 4L, 0);
514 #if PY_VERSION_HEX < 0x02040000
520 #if PY_VERSION_HEX < 0x02040000
515 PyMarshal_WriteLongToFile(mtime, fp);
521 PyMarshal_WriteLongToFile(mtime, fp);
516 #else
522 #else
517 PyMarshal_WriteLongToFile(mtime, fp, Py_MARSHAL_VERSION);
523 PyMarshal_WriteLongToFile(mtime, fp, Py_MARSHAL_VERSION);
518 #endif
524 #endif
519 fflush(fp);
525 fflush(fp);
520 fclose(fp);
526 fclose(fp);
521 if (Py_VerboseFlag)
527 if (Py_VerboseFlag)
522 PySys_WriteStderr("# wrote %s\n", filename.toLatin1().constData());
528 PySys_WriteStderr("# wrote %s\n", filename.toLatin1().constData());
523 //#ifdef macintosh
529 //#ifdef macintosh
524 // PyMac_setfiletype(cpathname, 'Pyth', 'PYC ');
530 // PyMac_setfiletype(cpathname, 'Pyth', 'PYC ');
525 //#endif
531 //#endif
526 }
532 }
527
533
528 /* Given the contents of a .py[co] file in a buffer, unmarshal the data
534 /* Given the contents of a .py[co] file in a buffer, unmarshal the data
529 and return the code object. Return None if it the magic word doesn't
535 and return the code object. Return None if it the magic word doesn't
530 match (we do this instead of raising an exception as we fall back
536 match (we do this instead of raising an exception as we fall back
531 to .py if available and we don't want to mask other errors).
537 to .py if available and we don't want to mask other errors).
532 Returns a new reference. */
538 Returns a new reference. */
533 PyObject *
539 PyObject *
534 PythonQtImport::unmarshalCode(const QString& path, const QByteArray& data, time_t mtime)
540 PythonQtImport::unmarshalCode(const QString& path, const QByteArray& data, time_t mtime)
535 {
541 {
536 PyObject *code;
542 PyObject *code;
537 // ugly cast, but Python API is not const safe
543 // ugly cast, but Python API is not const safe
538 char *buf = (char*) data.constData();
544 char *buf = (char*) data.constData();
539 int size = data.size();
545 int size = data.size();
540
546
541 if (size <= 9) {
547 if (size <= 9) {
542 PySys_WriteStderr("# %s has bad pyc data\n",
548 PySys_WriteStderr("# %s has bad pyc data\n",
543 path.toLatin1().constData());
549 path.toLatin1().constData());
544 Py_INCREF(Py_None);
550 Py_INCREF(Py_None);
545 return Py_None;
551 return Py_None;
546 }
552 }
547
553
548 if (getLong((unsigned char *)buf) != PyImport_GetMagicNumber()) {
554 if (getLong((unsigned char *)buf) != PyImport_GetMagicNumber()) {
549 if (Py_VerboseFlag)
555 if (Py_VerboseFlag)
550 PySys_WriteStderr("# %s has bad magic\n",
556 PySys_WriteStderr("# %s has bad magic\n",
551 path.toLatin1().constData());
557 path.toLatin1().constData());
552 Py_INCREF(Py_None);
558 Py_INCREF(Py_None);
553 return Py_None;
559 return Py_None;
554 }
560 }
555
561
556 if (mtime != 0 && !(getLong((unsigned char *)buf + 4) == mtime)) {
562 if (mtime != 0 && !(getLong((unsigned char *)buf + 4) == mtime)) {
557 if (Py_VerboseFlag)
563 if (Py_VerboseFlag)
558 PySys_WriteStderr("# %s has bad mtime\n",
564 PySys_WriteStderr("# %s has bad mtime\n",
559 path.toLatin1().constData());
565 path.toLatin1().constData());
560 Py_INCREF(Py_None);
566 Py_INCREF(Py_None);
561 return Py_None;
567 return Py_None;
562 }
568 }
563
569
564 code = PyMarshal_ReadObjectFromString(buf + 8, size - 8);
570 code = PyMarshal_ReadObjectFromString(buf + 8, size - 8);
565 if (code == NULL)
571 if (code == NULL)
566 return NULL;
572 return NULL;
567 if (!PyCode_Check(code)) {
573 if (!PyCode_Check(code)) {
568 Py_DECREF(code);
574 Py_DECREF(code);
569 PyErr_Format(PyExc_TypeError,
575 PyErr_Format(PyExc_TypeError,
570 "compiled module %.200s is not a code object",
576 "compiled module %.200s is not a code object",
571 path.toLatin1().constData());
577 path.toLatin1().constData());
572 return NULL;
578 return NULL;
573 }
579 }
574 return code;
580 return code;
575 }
581 }
576
582
577
583
578 /* Given a string buffer containing Python source code, compile it
584 /* Given a string buffer containing Python source code, compile it
579 return and return a code object as a new reference. */
585 return and return a code object as a new reference. */
580 PyObject *
586 PyObject *
581 PythonQtImport::compileSource(const QString& path, const QByteArray& data)
587 PythonQtImport::compileSource(const QString& path, const QByteArray& data)
582 {
588 {
583 PyObject *code;
589 PyObject *code;
584 QByteArray data1 = data;
590 QByteArray data1 = data;
585 // in qt4, data is null terminated
591 // in qt4, data is null terminated
586 // data1.resize(data.size()+1);
592 // data1.resize(data.size()+1);
587 // data1.data()[data.size()-1] = 0;
593 // data1.data()[data.size()-1] = 0;
588 code = Py_CompileString(data.data(), path.toLatin1().constData(),
594 code = Py_CompileString(data.data(), path.toLatin1().constData(),
589 Py_file_input);
595 Py_file_input);
590 return code;
596 return code;
591 }
597 }
592
598
593
599
594 /* Return the code object for the module named by 'fullname' from the
600 /* Return the code object for the module named by 'fullname' from the
595 Zip archive as a new reference. */
601 Zip archive as a new reference. */
596 PyObject *
602 PyObject *
597 PythonQtImport::getCodeFromData(const QString& path, int isbytecode,int /*ispackage*/, time_t mtime)
603 PythonQtImport::getCodeFromData(const QString& path, int isbytecode,int /*ispackage*/, time_t mtime)
598 {
604 {
599 PyObject *code;
605 PyObject *code;
600
606
601 QByteArray qdata;
607 QByteArray qdata;
602 if (!isbytecode) {
608 if (!isbytecode) {
603 // mlabDebugConst("MLABPython", "reading source " << path);
609 // mlabDebugConst("MLABPython", "reading source " << path);
604 bool ok;
610 bool ok;
605 qdata = PythonQt::importInterface()->readSourceFile(path, ok);
611 qdata = PythonQt::importInterface()->readSourceFile(path, ok);
606 if (!ok) {
612 if (!ok) {
607 // mlabErrorConst("PythonQtImporter","File could not be verified" << path);
613 // mlabErrorConst("PythonQtImporter","File could not be verified" << path);
608 return NULL;
614 return NULL;
609 }
615 }
610 if (qdata == " ") {
616 if (qdata == " ") {
611 qdata.clear();
617 qdata.clear();
612 }
618 }
613 } else {
619 } else {
614 qdata = PythonQt::importInterface()->readFileAsBytes(path);
620 qdata = PythonQt::importInterface()->readFileAsBytes(path);
615 }
621 }
616
622
617 if (isbytecode) {
623 if (isbytecode) {
618 // mlabDebugConst("MLABPython", "reading bytecode " << path);
624 // mlabDebugConst("MLABPython", "reading bytecode " << path);
619 code = unmarshalCode(path, qdata, mtime);
625 code = unmarshalCode(path, qdata, mtime);
620 }
626 }
621 else {
627 else {
622 // mlabDebugConst("MLABPython", "compiling source " << path);
628 // mlabDebugConst("MLABPython", "compiling source " << path);
623 code = compileSource(path, qdata);
629 code = compileSource(path, qdata);
624 if (code) {
630 if (code) {
625 // save a pyc file if possible
631 // save a pyc file if possible
626 QDateTime time;
632 QDateTime time;
627 time = PythonQt::importInterface()->lastModifiedDate(path);
633 time = PythonQt::importInterface()->lastModifiedDate(path);
628 writeCompiledModule((PyCodeObject*)code, path+"c", time.toTime_t());
634 writeCompiledModule((PyCodeObject*)code, path+"c", time.toTime_t());
629 }
635 }
630 }
636 }
631 return code;
637 return code;
632 }
638 }
633
639
634 time_t
640 time_t
635 PythonQtImport::getMTimeOfSource(const QString& path)
641 PythonQtImport::getMTimeOfSource(const QString& path)
636 {
642 {
637 time_t mtime = 0;
643 time_t mtime = 0;
638 QString path2 = path;
644 QString path2 = path;
639 path2.truncate(path.length()-1);
645 path2.truncate(path.length()-1);
640
646
641 if (PythonQt::importInterface()->exists(path2)) {
647 if (PythonQt::importInterface()->exists(path2)) {
642 mtime = PythonQt::importInterface()->lastModifiedDate(path2).toTime_t();
648 mtime = PythonQt::importInterface()->lastModifiedDate(path2).toTime_t();
643 }
649 }
644
650
645 return mtime;
651 return mtime;
646 }
652 }
647
653
648 /* Get the code object associated with the module specified by
654 /* Get the code object associated with the module specified by
649 'fullname'. */
655 'fullname'. */
650 PyObject *
656 PyObject *
651 PythonQtImport::getModuleCode(PythonQtImporter *self, char *fullname,
657 PythonQtImport::getModuleCode(PythonQtImporter *self, const char* fullname, QString& modpath)
652 int *p_ispackage, QString& modpath)
653 {
658 {
654 QString subname;
659 QString subname;
655 struct st_mlab_searchorder *zso;
660 struct st_mlab_searchorder *zso;
656
661
657 subname = getSubName(fullname);
662 subname = getSubName(fullname);
658 QString path = *self->_path + "/" + subname;
663 QString path = *self->_path + "/" + subname;
659
664
660 QString test;
665 QString test;
661 for (zso = mlab_searchorder; *zso->suffix; zso++) {
666 for (zso = mlab_searchorder; *zso->suffix; zso++) {
662 PyObject *code = NULL;
667 PyObject *code = NULL;
663 test = path + zso->suffix;
668 test = path + zso->suffix;
664
669
665 if (Py_VerboseFlag > 1)
670 if (Py_VerboseFlag > 1)
666 PySys_WriteStderr("# trying %s\n",
671 PySys_WriteStderr("# trying %s\n",
667 test.toLatin1().constData());
672 test.toLatin1().constData());
668 if (PythonQt::importInterface()->exists(test)) {
673 if (PythonQt::importInterface()->exists(test)) {
669 time_t mtime = 0;
674 time_t mtime = 0;
670 int ispackage = zso->type & IS_PACKAGE;
675 int ispackage = zso->type & IS_PACKAGE;
671 int isbytecode = zso->type & IS_BYTECODE;
676 int isbytecode = zso->type & IS_BYTECODE;
672
677
673 if (isbytecode)
678 if (isbytecode) {
674 mtime = getMTimeOfSource(test);
679 mtime = getMTimeOfSource(test);
675 if (p_ispackage != NULL)
680 }
676 *p_ispackage = ispackage;
677 code = getCodeFromData(test, isbytecode, ispackage, mtime);
681 code = getCodeFromData(test, isbytecode, ispackage, mtime);
678 if (code == Py_None) {
682 if (code == Py_None) {
679 Py_DECREF(code);
683 Py_DECREF(code);
680 continue;
684 continue;
681 }
685 }
682 if (code != NULL)
686 if (code != NULL) {
683 modpath = test;
687 modpath = test;
688 }
684 return code;
689 return code;
685 }
690 }
686 }
691 }
687 PyErr_Format(PythonQtImportError, "can't find module '%.200s'", fullname);
692 PyErr_Format(PythonQtImportError, "can't find module '%.200s'", fullname);
688
693
689 return NULL;
694 return NULL;
690 }
695 }
691
696
692 QString PythonQtImport::replaceExtension(const QString& str, const QString& ext)
697 QString PythonQtImport::replaceExtension(const QString& str, const QString& ext)
693 {
698 {
694 QString r;
699 QString r;
695 int i = str.lastIndexOf('.');
700 int i = str.lastIndexOf('.');
696 if (i!=-1) {
701 if (i!=-1) {
697 r = str.mid(0,i) + "." + ext;
702 r = str.mid(0,i) + "." + ext;
698 } else {
703 } else {
699 r = str + "." + ext;
704 r = str + "." + ext;
700 }
705 }
701 return r;
706 return r;
702 }
707 }
703
708
704 PyObject* PythonQtImport::getCodeFromPyc(const QString& file)
709 PyObject* PythonQtImport::getCodeFromPyc(const QString& file)
705 {
710 {
706 PyObject* code;
711 PyObject* code;
707 const static QString pycStr("pyc");
712 const static QString pycStr("pyc");
708 QString pyc = replaceExtension(file, pycStr);
713 QString pyc = replaceExtension(file, pycStr);
709 if (PythonQt::importInterface()->exists(pyc)) {
714 if (PythonQt::importInterface()->exists(pyc)) {
710 time_t mtime = 0;
715 time_t mtime = 0;
711 mtime = getMTimeOfSource(pyc);
716 mtime = getMTimeOfSource(pyc);
712 code = getCodeFromData(pyc, true, false, mtime);
717 code = getCodeFromData(pyc, true, false, mtime);
713 if (code != Py_None && code != NULL) {
718 if (code != Py_None && code != NULL) {
714 return code;
719 return code;
715 }
720 }
716 if (code) {
721 if (code) {
717 Py_DECREF(code);
722 Py_DECREF(code);
718 }
723 }
719 }
724 }
720 code = getCodeFromData(file,false,false,0);
725 code = getCodeFromData(file,false,false,0);
721 return code;
726 return code;
722 }
727 }
723
728
724 /* Module init */
729 /* Module init */
725
730
726 PyDoc_STRVAR(mlabimport_doc,
731 PyDoc_STRVAR(mlabimport_doc,
727 "Imports python files into PythonQt, completely replaces internal python import");
732 "Imports python files into PythonQt, completely replaces internal python import");
728
733
729 void PythonQtImport::init()
734 void PythonQtImport::init()
730 {
735 {
731 static bool first = true;
736 static bool first = true;
732 if (!first) {
737 if (!first) {
733 return;
738 return;
734 }
739 }
735 first = false;
740 first = false;
736
741
737 PyObject *mod;
742 PyObject *mod;
738
743
739 if (PyType_Ready(&PythonQtImporter_Type) < 0)
744 if (PyType_Ready(&PythonQtImporter_Type) < 0)
740 return;
745 return;
741
746
742 /* Correct directory separator */
747 /* Correct directory separator */
743 mlab_searchorder[0].suffix[0] = SEP;
748 mlab_searchorder[0].suffix[0] = SEP;
744 mlab_searchorder[1].suffix[0] = SEP;
749 mlab_searchorder[1].suffix[0] = SEP;
745 mlab_searchorder[2].suffix[0] = SEP;
750 mlab_searchorder[2].suffix[0] = SEP;
746 if (Py_OptimizeFlag) {
751 if (Py_OptimizeFlag) {
747 /* Reverse *.pyc and *.pyo */
752 /* Reverse *.pyc and *.pyo */
748 struct st_mlab_searchorder tmp;
753 struct st_mlab_searchorder tmp;
749 tmp = mlab_searchorder[0];
754 tmp = mlab_searchorder[0];
750 mlab_searchorder[0] = mlab_searchorder[1];
755 mlab_searchorder[0] = mlab_searchorder[1];
751 mlab_searchorder[1] = tmp;
756 mlab_searchorder[1] = tmp;
752 tmp = mlab_searchorder[3];
757 tmp = mlab_searchorder[3];
753 mlab_searchorder[3] = mlab_searchorder[4];
758 mlab_searchorder[3] = mlab_searchorder[4];
754 mlab_searchorder[4] = tmp;
759 mlab_searchorder[4] = tmp;
755 }
760 }
756
761
757 mod = Py_InitModule4("PythonQtImport", NULL, mlabimport_doc,
762 mod = Py_InitModule4("PythonQtImport", NULL, mlabimport_doc,
758 NULL, PYTHON_API_VERSION);
763 NULL, PYTHON_API_VERSION);
759
764
760 PythonQtImportError = PyErr_NewException("PythonQtImport.PythonQtImportError",
765 PythonQtImportError = PyErr_NewException("PythonQtImport.PythonQtImportError",
761 PyExc_ImportError, NULL);
766 PyExc_ImportError, NULL);
762 if (PythonQtImportError == NULL)
767 if (PythonQtImportError == NULL)
763 return;
768 return;
764
769
765 Py_INCREF(PythonQtImportError);
770 Py_INCREF(PythonQtImportError);
766 if (PyModule_AddObject(mod, "PythonQtImportError",
771 if (PyModule_AddObject(mod, "PythonQtImportError",
767 PythonQtImportError) < 0)
772 PythonQtImportError) < 0)
768 return;
773 return;
769
774
770 Py_INCREF(&PythonQtImporter_Type);
775 Py_INCREF(&PythonQtImporter_Type);
771 if (PyModule_AddObject(mod, "PythonQtImporter",
776 if (PyModule_AddObject(mod, "PythonQtImporter",
772 (PyObject *)&PythonQtImporter_Type) < 0)
777 (PyObject *)&PythonQtImporter_Type) < 0)
773 return;
778 return;
774
779
775 // set our importer into the path_hooks to handle all path on sys.path
780 // set our importer into the path_hooks to handle all path on sys.path
776 PyObject* classobj = PyDict_GetItemString(PyModule_GetDict(mod), "PythonQtImporter");
781 PyObject* classobj = PyDict_GetItemString(PyModule_GetDict(mod), "PythonQtImporter");
777 PyObject* path_hooks = PySys_GetObject("path_hooks");
782 PyObject* path_hooks = PySys_GetObject("path_hooks");
778 PyList_Append(path_hooks, classobj);
783 PyList_Append(path_hooks, classobj);
779
784
780 #ifndef WIN32
785 #ifndef WIN32
781 // reload the encodings module, because it might fail to custom import requirements (e.g. encryption).
786 // reload the encodings module, because it might fail to custom import requirements (e.g. encryption).
782 PyObject* modules = PyImport_GetModuleDict();
787 PyObject* modules = PyImport_GetModuleDict();
783 PyObject* encodingsModule = PyDict_GetItemString(modules, "encodings");
788 PyObject* encodingsModule = PyDict_GetItemString(modules, "encodings");
784 if (encodingsModule != NULL) {
789 if (encodingsModule != NULL) {
785 PyImport_ReloadModule(encodingsModule);
790 PyImport_ReloadModule(encodingsModule);
786 }
791 }
787 #endif
792 #endif
788 }
793 }
@@ -1,128 +1,137
1 #ifndef _PYTHONQTIMPORTER_
1 #ifndef _PYTHONQTIMPORTER_
2 #define _PYTHONQTIMPORTER_
2 #define _PYTHONQTIMPORTER_
3
3
4 /*
4 /*
5 *
5 *
6 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
6 * Copyright (C) 2006 MeVis Research GmbH All Rights Reserved.
7 *
7 *
8 * This library is free software; you can redistribute it and/or
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
11 * version 2.1 of the License, or (at your option) any later version.
12 *
12 *
13 * This library is distributed in the hope that it will be useful,
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
16 * Lesser General Public License for more details.
17 *
17 *
18 * Further, this software is distributed without any warranty that it is
18 * Further, this software is distributed without any warranty that it is
19 * free of the rightful claim of any third person regarding infringement
19 * free of the rightful claim of any third person regarding infringement
20 * or the like. Any license provided herein, whether implied or
20 * or the like. Any license provided herein, whether implied or
21 * otherwise, applies only to this software file. Patent licenses, if
21 * otherwise, applies only to this software file. Patent licenses, if
22 * any, provided herein do not apply to combinations of this program with
22 * any, provided herein do not apply to combinations of this program with
23 * other software, or any other product whatsoever.
23 * other software, or any other product whatsoever.
24 *
24 *
25 * You should have received a copy of the GNU Lesser General Public
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 *
28 *
29 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
29 * Contact information: MeVis Research GmbH, Universitaetsallee 29,
30 * 28359 Bremen, Germany or:
30 * 28359 Bremen, Germany or:
31 *
31 *
32 * http://www.mevis.de
32 * http://www.mevis.de
33 *
33 *
34 */
34 */
35
35
36 //----------------------------------------------------------------------------------
36 //----------------------------------------------------------------------------------
37 /*!
37 /*!
38 // \file PythonQtImporter.h
38 // \file PythonQtImporter.h
39 // \author Florian Link
39 // \author Florian Link
40 // \author Last changed by $Author: stk $
40 // \author Last changed by $Author: stk $
41 // \date 2004-06
41 // \date 2004-06
42 */
42 */
43 //----------------------------------------------------------------------------------
43 //----------------------------------------------------------------------------------
44
44
45 #include "Python.h"
45 #include "Python.h"
46 #include "structmember.h"
46 #include "structmember.h"
47 #include "osdefs.h"
47 #include "osdefs.h"
48 #include "marshal.h"
48 #include "marshal.h"
49 #include "compile.h"
49 #include "compile.h"
50 #include <time.h>
50 #include <time.h>
51
51
52 #include <qobject.h>
52 #include <qobject.h>
53 #include <qstring.h>
53 #include <qstring.h>
54
54
55
55
56 //! defines a python object that stores a Qt slot info
56 //! defines a python object that stores a Qt slot info
57 typedef struct _PythonQtImporter {
57 typedef struct _PythonQtImporter {
58 PyObject_HEAD
58 PyObject_HEAD
59 QString* _path;
59 QString* _path;
60 } PythonQtImporter;
60 } PythonQtImporter;
61
61
62
62
63 //! implements importing of python files into PythonQt
63 //! implements importing of python files into PythonQt
64 /*! also compiles/marshalls/unmarshalls py/pyc files and handles time stamps correctly
64 /*! also compiles/marshalls/unmarshalls py/pyc files and handles time stamps correctly
65 */
65 */
66 class PythonQtImport
66 class PythonQtImport
67 {
67 {
68 public:
68 public:
69 enum module_info {
69
70 MI_ERROR,
70 enum ModuleType {
71 MI_NOT_FOUND,
71 MI_NOT_FOUND,
72 MI_MODULE,
72 MI_MODULE,
73 MI_PACKAGE,
73 MI_PACKAGE,
74 MI_SHAREDLIBRARY
74 MI_SHAREDLIBRARY
75 };
75 };
76
76
77 struct ModuleInfo {
78 ModuleInfo() {
79 type = MI_NOT_FOUND;
80 }
81 QString fullPath; //!< the full path to the found file
82 QString moduleName; //!< the module name without the package prefix
83 ModuleType type;
84 };
85
77 //! initialize
86 //! initialize
78 static void init();
87 static void init();
79
88
80 //! writes the python code to disk, marshalling and writing the time stamp
89 //! writes the python code to disk, marshalling and writing the time stamp
81 static void writeCompiledModule(PyCodeObject *co, const QString& filename, long mtime);
90 static void writeCompiledModule(PyCodeObject *co, const QString& filename, long mtime);
82
91
83 /*! Given the contents of a .py[co] file in a buffer, unmarshal the data
92 /*! Given the contents of a .py[co] file in a buffer, unmarshal the data
84 and return the code object. Return None if it the magic word doesn't
93 and return the code object. Return None if it the magic word doesn't
85 match (we do this instead of raising an exception as we fall back
94 match (we do this instead of raising an exception as we fall back
86 to .py if available and we don't want to mask other errors).
95 to .py if available and we don't want to mask other errors).
87 Returns a new reference. */
96 Returns a new reference. */
88 static PyObject *unmarshalCode(const QString& path, const QByteArray& data, time_t mtime);
97 static PyObject *unmarshalCode(const QString& path, const QByteArray& data, time_t mtime);
89
98
90 //! Given a string buffer containing Python source code, compile it
99 //! Given a string buffer containing Python source code, compile it
91 //! return and return a code object as a new reference.
100 //! return and return a code object as a new reference.
92 static PyObject *compileSource(const QString& path, const QByteArray& data);
101 static PyObject *compileSource(const QString& path, const QByteArray& data);
93
102
94 //! Return the code object for the module named by 'fullname' from the
103 //! Return the code object for the module named by 'fullname' from the
95 //! Zip archive as a new reference.
104 //! Zip archive as a new reference.
96 static PyObject *getCodeFromData(const QString& path, int isbytecode = 0, int ispackage = 0,
105 static PyObject *getCodeFromData(const QString& path, int isbytecode = 0, int ispackage = 0,
97 time_t mtime = 0);
106 time_t mtime = 0);
98
107
99 //! Get the code object associated with the module specified by
108 //! Get the code object associated with the module specified by
100 //! 'fullname'.
109 //! 'fullname'.
101 static PyObject * getModuleCode(PythonQtImporter *self, char *fullname,
110 static PyObject * getModuleCode(PythonQtImporter *self,
102 int *p_ispackage, QString& modpath);
111 const char* fullname, QString& modpath);
103
112
104
113
105 //! gets the compiled code for the given *.py file if there is a valid pyc file, otherwise compiles the file and writes the pyc
114 //! gets the compiled code for the given *.py file if there is a valid pyc file, otherwise compiles the file and writes the pyc
106 static PyObject* getCodeFromPyc(const QString& file);
115 static PyObject* getCodeFromPyc(const QString& file);
107
116
108 //! Return if module exists and is a package or a module
117 //! Return if module exists and is a package or a module
109 static module_info getModuleInfo(PythonQtImporter* self, const QString& fullname);
118 static ModuleInfo getModuleInfo(PythonQtImporter* self, const QString& fullname);
110
119
111 //! get the last name of a dot chain (first.second.last)
120 //! get the last name of a dot chain (first.second.last)
112 static QString getSubName(const QString& str);
121 static QString getSubName(const QString& str);
113
122
114 //! Given a buffer, return the long that is represented by the first
123 //! Given a buffer, return the long that is represented by the first
115 //! 4 bytes, encoded as little endian. This partially reimplements
124 //! 4 bytes, encoded as little endian. This partially reimplements
116 //! marshal.c:r_long()
125 //! marshal.c:r_long()
117 static long getLong(unsigned char *buf);
126 static long getLong(unsigned char *buf);
118
127
119 //! get time stamp of file
128 //! get time stamp of file
120 static time_t getMTimeOfSource(const QString& path);
129 static time_t getMTimeOfSource(const QString& path);
121
130
122 //! replace extension of file
131 //! replace extension of file
123 static QString replaceExtension(const QString& str, const QString& ext);
132 static QString replaceExtension(const QString& str, const QString& ext);
124
133
125 };
134 };
126
135
127 #endif
136 #endif
128
137
General Comments 0
You need to be logged in to leave comments. Login now