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