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