scidavis  2.3.0
About: SciDAVis is a free application for Scientific Data Analysis and Visualization (a fork off of QtiPlot).
  Fossies Dox: scidavis-2.3.0.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

PythonScripting.cpp
Go to the documentation of this file.
1 
11 
29 // get rid of a compiler warning
30 #ifdef _POSIX_C_SOURCE
31 #undef _POSIX_C_SOURCE
32 #endif
33 #include <Python.h>
34 #include <compile.h>
35 #include <eval.h>
36 #include <frameobject.h>
37 #include <traceback.h>
38 
39 #include <iostream>
40 
41 #define str(x) xstr(x)
42 #define xstr(x) #x
43 
44 #if PY_VERSION_HEX < 0x020400A1
45 typedef struct _traceback {
46  PyObject_HEAD
48  PyFrameObject *tb_frame;
49  int tb_lasti;
50  int tb_lineno;
52 #endif
53 
54 #include "PythonScript.h"
55 #include "PythonScripting.h"
56 #include "ApplicationWindow.h"
57 
58 #include <QObject>
59 #include <QStringList>
60 #include <QDir>
61 #include <QDateTime>
62 #include <QCoreApplication>
63 
64 // includes sip.h, which undefines Qt's "slots" macro since SIP 4.6
65 #include "sipAPIscidavis.h"
66 extern "C"
67 {
68 #if PY_MAJOR_VERSION < 3
69  void initsip();
70  void initQtCore();
71  void initQtGui();
72  void initscidavis();
73 #define PYUNICODE_AsUTF8 PyString_AsString
74 #define PYUNICODE_FromString PyString_FromString
75 #define PYLong_AsLong PyInt_AsLong
76 #define PYCodeObject_cast (PyCodeObject*)
77 #else
78  PyMODINIT_FUNC PyInit_scidavis(void);
79 #define PYUNICODE_AsUTF8 PyUnicode_AsUTF8
80 #define PYUNICODE_FromString PyUnicode_FromString
81 #define PYLong_AsLong PyLong_AsLong
82 #define PYCodeObject_cast
83 #endif
84 }
85 
86 const char* PythonScripting::langName = "Python";
87 
88 QString PythonScripting::toString(PyObject *object, bool decref)
89 {
90  QString ret;
91  if (!object) return "";
92  PyObject *repr = PyObject_Str(object);
93  if (decref) Py_DECREF(object);
94  if (!repr) return "";
95  ret = PYUNICODE_AsUTF8(repr);
96  Py_DECREF(repr);
97  return ret;
98 }
99 
100 PyObject *PythonScripting::eval(const QString &code, PyObject *argDict, const char *name)
101 {
102  PyObject *args;
103  if (argDict)
104  args = argDict;
105  else
106  args = globals;
107  PyObject *ret=NULL;
108  PyObject *co = Py_CompileString(code.toUtf8().constData(), name, Py_eval_input);
109  if (co)
110  {
111  ret = PyEval_EvalCode(PYCodeObject_cast co, globals, args);
112  Py_DECREF(co);
113  }
114  return ret;
115 }
116 
117 bool PythonScripting::exec (const QString &code, PyObject *argDict, const char *name)
118 {
119  PyObject *args;
120  if (argDict)
121  args = argDict;
122  else
123  args = globals;
124  PyObject *tmp = NULL;
125  PyObject *co = Py_CompileString(code.toUtf8().constData(), name, Py_file_input);
126  if (co)
127  {
128  tmp = PyEval_EvalCode(PYCodeObject_cast co, globals, args);
129  Py_DECREF(co);
130  }
131  if (!tmp) return false;
132  Py_DECREF(tmp);
133  return true;
134 }
135 
137 {
138  PyObject *exception=0, *value=0, *traceback=0;
139  PyTracebackObject *excit=0;
140  PyFrameObject *frame;
141  const char *fname;
142  QString msg;
143  if (!PyErr_Occurred()) return "";
144 
145  PyErr_Fetch(&exception, &value, &traceback);
146  PyErr_NormalizeException(&exception, &value, &traceback);
147  if(PyErr_GivenExceptionMatches(exception, PyExc_SyntaxError))
148  {
149  QString text = toString(PyObject_GetAttrString(value, "text"), true);
150  msg.append(text + "\n");
151  PyObject *offset = PyObject_GetAttrString(value, "offset");
152  for (int i=0; i<(PYLong_AsLong(offset)-1); i++)
153  if (text[i] == '\t')
154  msg.append("\t");
155  else
156  msg.append(" ");
157  msg.append("^\n");
158  Py_DECREF(offset);
159  msg.append("SyntaxError: ");
160  msg.append(toString(PyObject_GetAttrString(value, "msg"), true) + "\n");
161  msg.append("at ").append(toString(PyObject_GetAttrString(value, "filename"), true));
162  msg.append(":").append(toString(PyObject_GetAttrString(value, "lineno"), true));
163  msg.append("\n");
164  Py_DECREF(exception);
165  Py_DECREF(value);
166  } else {
167  msg.append(toString(exception,true)).remove("exceptions.").append(": ");
168  msg.append(toString(value,true));
169  msg.append("\n");
170  }
171 
172  if (traceback) {
173  excit = (PyTracebackObject*)traceback;
174  while (excit && (PyObject*)excit != Py_None)
175  {
176  frame = excit->tb_frame;
177  msg.append("at ").append(PYUNICODE_AsUTF8(frame->f_code->co_filename));
178  msg.append(":").append(QString::number(excit->tb_lineno));
179  if (frame->f_code->co_name && *(fname = PYUNICODE_AsUTF8(frame->f_code->co_name)) != '?')
180  msg.append(" in ").append(fname);
181  msg.append("\n");
182  excit = excit->tb_next;
183  }
184  Py_DECREF(traceback);
185  }
186 
187  return msg;
188 }
189 
191  : ScriptingEnv(parent, langName)
192 {
193  Q_UNUSED(batch)
194  PyObject *mainmod=NULL, *scidavismod=NULL, *sysmod=NULL;
195  math = NULL;
196  sys = NULL;
197  d_initialized = false;
198  if (Py_IsInitialized())
199  {
200  // PyEval_AcquireLock();
201  mainmod = PyImport_ImportModule("__main__");
202  if (!mainmod)
203  {
204  PyErr_Print();
205  // PyEval_ReleaseLock();
206  return;
207  }
208  globals = PyModule_GetDict(mainmod);
209  Py_DECREF(mainmod);
210  } else {
211  // if we need to bundle Python libraries with the executable,
212  // specify the library location here
213 #ifdef PYTHONHOME
214  Py_SetPythonHome(const_cast<char*>(str(PYTHONHOME)));
215 #endif
216  // PyEval_InitThreads ();
217 #if PY_MAJOR_VERSION >= 3
218  PyImport_AppendInittab("scidavis", &PyInit_scidavis);
219 #endif
220  Py_Initialize ();
221  if (!Py_IsInitialized ())
222  return;
223 
224 
225 #if PY_MAJOR_VERSION < 3
226 #ifdef SIP_STATIC_MODULE
227  initsip();
228  initQtCore();
229  initQtGui();
230 #endif
231  initscidavis();
232 #endif
233  mainmod = PyImport_AddModule("__main__");
234  if (!mainmod)
235  {
236  // PyEval_ReleaseLock();
237  PyErr_Print();
238  return;
239  }
240  globals = PyModule_GetDict(mainmod);
241  }
242 
243  if (!globals)
244  {
245  PyErr_Print();
246  // PyEval_ReleaseLock();
247  return;
248  }
249  Py_INCREF(globals);
250 
251  math = PyDict_New();
252  if (!math)
253  PyErr_Print();
254 
255  scidavismod = PyImport_ImportModule("scidavis");
256  if (scidavismod)
257  {
258  PyDict_SetItemString(globals, "scidavis", scidavismod);
259  PyObject *scidavisDict = PyModule_GetDict(scidavismod);
260  if (!setQObject(d_parent, "app", scidavisDict))
261  QMessageBox::warning
262  (d_parent, tr("Failed to export SciDAVis API"),
263  tr("Accessing SciDAVis functions or objects from Python code won't work."
264  "Probably your version of SIP differs from the one SciDAVis was compiled against;"
265  "try updating SIP or recompiling SciDAVis."));
266  PyDict_SetItemString(scidavisDict, "mathFunctions", math);
267  Py_DECREF(scidavismod);
268  } else
269  PyErr_Print();
270 
271  sysmod = PyImport_ImportModule("sys");
272  if (sysmod)
273  {
274  sys = PyModule_GetDict(sysmod);
275  Py_INCREF(sys);
276  } else
277  PyErr_Print();
278 
279  // PyEval_ReleaseLock();
280  d_initialized = true;
281 }
282 
284 {
285  // Redirect output to the print(const QString&) signal.
286  // Also see method write(const QString&) and Python documentation on
287  // sys.stdout and sys.stderr.
288  setQObject(this, "stdout", sys);
289  setQObject(this, "stderr", sys);
290 }
291 
293 {
294  if (!d_initialized) return false;
295  // PyEval_AcquireLock();
296 
297  if (!d_parent->batchMode())
298  {
299  // Redirect output to the print(const QString&) signal.
300  // Also see method write(const QString&) and Python documentation on
301  // sys.stdout and sys.stderr.
302  setQObject(this, "stdout", sys);
303  setQObject(this, "stderr", sys);
304  }
305  bool initialized;
306  initialized = loadInitFile(QDir::homePath()+"/scidavisrc");
307  if(!initialized) initialized = loadInitFile(QDir::homePath()+"/.scidavisrc");
308 #ifdef PYTHON_CONFIG_PATH
309  if(!initialized) initialized = loadInitFile(PYTHON_CONFIG_PATH"/scidavisrc");
310  if(!initialized) initialized = loadInitFile(PYTHON_CONFIG_PATH"/.scidavisrc");
311 #endif
312  if(!initialized) initialized = loadInitFile(QDir::rootPath()+"etc/scidavisrc");
313  if(!initialized) initialized = loadInitFile(QCoreApplication::instance()->applicationDirPath()+"/scidavisrc");
314  if(!initialized) initialized = loadInitFile("scidavisrc");
315 
316  // PyEval_ReleaseLock();
317  return true;
318 }
319 
321 {
322  Py_XDECREF(globals);
323  Py_XDECREF(math);
324  Py_XDECREF(sys);
325 }
326 
327 #ifndef PYTHON_UTIL_PATH
328 #define PYTHON_UTIL_PATH "."
329 #endif
330 
331 bool PythonScripting::loadInitFile(const QString &path)
332 {
333  PyRun_SimpleString("import sys\nsys.path.append('" PYTHON_UTIL_PATH "')");
334  QFileInfo pyFile(path+".py"), pycFile(path+".pyc");
335  bool success = false;
336  if (pycFile.isReadable() && (pycFile.lastModified() >= pyFile.lastModified())) {
337  // if we have a recent pycFile, use it
338  FILE *f = fopen(pycFile.filePath().toLocal8Bit(), "rb");
339  success = PyRun_SimpleFileEx(f, pycFile.filePath().toLocal8Bit(), false) == 0;
340  fclose(f);
341  } else if (pyFile.isReadable() && pyFile.exists()) {
342  // try to compile pyFile to pycFile
343  PyObject *compileModule = PyImport_ImportModule("py_compile");
344  if (compileModule) {
345  PyObject *compile = PyDict_GetItemString(PyModule_GetDict(compileModule), "compile");
346  if (compile) {
347  PyObject *tmp = PyObject_CallFunctionObjArgs(compile,
348  PYUNICODE_FromString(pyFile.filePath().toUtf8().constData()),
349  PYUNICODE_FromString(pycFile.filePath().toUtf8().constData()),
350  NULL);
351  if (tmp)
352  Py_DECREF(tmp);
353  else
354  PyErr_Print();
355  } else
356  PyErr_Print();
357  Py_DECREF(compileModule);
358  } else
359  PyErr_Print();
360  pycFile.refresh();
361  if (pycFile.isReadable() && (pycFile.lastModified() >= pyFile.lastModified())) {
362  // run the newly compiled pycFile
363  FILE *f = fopen(pycFile.filePath().toLocal8Bit(), "rb");
364  success = PyRun_SimpleFileEx(f, pycFile.filePath().toLocal8Bit(), false) == 0;
365  fclose(f);
366  } else {
367  // fallback: just run pyFile
368  /*FILE *f = fopen(pyFile.filePath(), "r");
369  success = PyRun_SimpleFileEx(f, pyFile.filePath(), false) == 0;
370  fclose(f);*/
371  //TODO: code above crashes on Windows - bug in Python?
372  QFile f(pyFile.filePath());
373  if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
374  QByteArray data = f.readAll();
375  success = PyRun_SimpleString(data.data());
376  f.close();
377  }
378  }
379  }
380  return success;
381 }
382 
384 {
385  return Py_IsInitialized();
386 }
387 
388 bool PythonScripting::setQObject(QObject *val, const char *name, PyObject *dict)
389 {
390  if(!val) return false;
391  PyObject *pyobj=NULL;
392 
393  PyGILState_STATE state = PyGILState_Ensure();
394 
395  //sipWrapperType * klass = sipFindClass(val->className());
396  const sipTypeDef* klass=sipFindType(val->metaObject()->className());
397  if (!klass) return false;
398  //pyobj = sipConvertFromInstance(val, klass, NULL);
399  pyobj = sipConvertFromType(val, klass, NULL);
400  if (!pyobj) return false;
401 
402  if (dict)
403  PyDict_SetItemString(dict,name,pyobj);
404  else
405  PyDict_SetItemString(globals,name,pyobj);
406  Py_DECREF(pyobj);
407 
408  PyGILState_Release(state);
409  return true;
410 }
411 
412 bool PythonScripting::setInt(int val, const char *name, PyObject *dict)
413 {
414  PyObject *pyobj = Py_BuildValue("i",val);
415  if (!pyobj) return false;
416  if (dict)
417  PyDict_SetItemString(dict,name,pyobj);
418  else
419  PyDict_SetItemString(globals,name,pyobj);
420  Py_DECREF(pyobj);
421  return true;
422 }
423 
424 bool PythonScripting::setDouble(double val, const char *name, PyObject *dict)
425 {
426  PyObject *pyobj = Py_BuildValue("d",val);
427  if (!pyobj) return false;
428  if (dict)
429  PyDict_SetItemString(dict,name,pyobj);
430  else
431  PyDict_SetItemString(globals,name,pyobj);
432  Py_DECREF(pyobj);
433  return true;
434 }
435 
436 const QStringList PythonScripting::mathFunctions() const
437 {
438  QStringList flist;
439  PyObject *key, *value;
440 #if PY_VERSION_HEX >= 0x02050000
441  Py_ssize_t i=0;
442 #else
443  int i=0;
444 #endif
445  while(PyDict_Next(math, &i, &key, &value))
446  if (PyCallable_Check(value))
447  flist << PYUNICODE_AsUTF8(key);
448  flist.sort();
449  return flist;
450 }
451 
452 const QString PythonScripting::mathFunctionDoc(const QString &name) const
453 {
454  PyObject *mathf = PyDict_GetItemString(math,name.toLocal8Bit()); // borrowed
455  if (!mathf) return "";
456  PyObject *pydocstr = PyObject_GetAttrString(mathf, "__doc__"); // new
457  QString qdocstr = PYUNICODE_AsUTF8(pydocstr);
458  Py_XDECREF(pydocstr);
459  return qdocstr;
460 }
461 
462 const QStringList PythonScripting::fileExtensions() const
463 {
464  QStringList extensions;
465  extensions << "py" << "PY";
466  return extensions;
467 }
468 
469 
PythonScripting::math
PyObject * math
Definition: PythonScripting.h:102
_traceback
Definition: PythonScripting.cpp:45
PythonScripting::exec
bool exec(const QString &code, PyObject *argDict=NULL, const char *name="<scidavis>")
execute a sequence of Python statements
Definition: PythonScripting.cpp:117
PythonScripting::setQObject
bool setQObject(QObject *, const char *, PyObject *dict)
Definition: PythonScripting.cpp:388
_traceback::tb_frame
PyFrameObject * tb_frame
Definition: PythonScripting.cpp:48
data
Definition: exp_saturation.c:37
PythonScripting::eval
PyObject * eval(const QString &code, PyObject *argDict=NULL, const char *name="<scidavis>")
evaluate a Python expression
Definition: PythonScripting.cpp:100
str
#define str(x)
Definition: PythonScripting.cpp:41
PythonScripting::setInt
bool setInt(int i, const char *s) override
Definition: PythonScripting.h:86
PythonScripting::toString
QString toString(PyObject *object, bool decref=false)
like str(object) in Python
Definition: PythonScripting.cpp:88
PythonScripting::fileExtensions
const QStringList fileExtensions() const override
Return a list of file extensions commonly used for this language.
Definition: PythonScripting.cpp:462
PythonScripting::globals
PyObject * globals
Definition: PythonScripting.h:101
ScriptingEnv::decref
void decref()
Decrease the reference count. This should only be called by scripted and Script to avoid segfaults.
Definition: ScriptingEnv.cpp:65
initQtGui
void initQtGui()
PythonScripting::initialize
bool initialize() override
Part of the initialization is deferred from the constructor until after the signals have been connect...
Definition: PythonScripting.cpp:292
PythonScripting::isRunning
bool isRunning() const override
whether asynchronuous execution is enabled (if supported by the implementation)
Definition: PythonScripting.cpp:383
ScriptingEnv::d_parent
ApplicationWindow * d_parent
the context in which we are running
Definition: ScriptingEnv.h:112
PythonScripting.h
ScriptingEnv::initialized
bool initialized() const
initialization of the interpreter may fail; or there could be other errors setting up the environment
Definition: ScriptingEnv.h:61
PythonScripting::mathFunctions
const QStringList mathFunctions() const override
Return a list of supported mathematical functions. These should be imported into the global namespace...
Definition: PythonScripting.cpp:436
initscidavis
void initscidavis()
PYLong_AsLong
#define PYLong_AsLong
Definition: PythonScripting.cpp:75
ScriptingEnv::d_initialized
bool d_initialized
whether the interpreter has been successfully initialized
Definition: ScriptingEnv.h:110
PythonScripting::langName
static const char * langName
Definition: PythonScripting.h:45
PythonScripting::errorMsg
QString errorMsg()
Definition: PythonScripting.cpp:136
initsip
void initsip()
PythonScripting::setDouble
bool setDouble(double x, const char *s) override
Definition: PythonScripting.h:88
PyTracebackObject
struct _traceback PyTracebackObject
PythonScripting::PythonScripting
PythonScripting(ApplicationWindow *parent, bool batch=false)
Definition: PythonScripting.cpp:190
PythonScripting::loadInitFile
bool loadInitFile(const QString &path)
Definition: PythonScripting.cpp:331
PythonScript.h
ScriptingEnv
An interpreter for evaluating scripting code. Abstract.
Definition: ScriptingEnv.h:53
ApplicationWindow::batchMode
bool batchMode() const
running a python batch script
Definition: ApplicationWindow.h:156
_traceback::tb_lasti
int tb_lasti
Definition: PythonScripting.cpp:49
ApplicationWindow
SciDAVis's main window.
Definition: ApplicationWindow.h:122
_traceback::tb_next
PyObject_HEAD struct _traceback * tb_next
Definition: PythonScripting.cpp:47
PYCodeObject_cast
#define PYCodeObject_cast
Definition: PythonScripting.cpp:76
name
char * name()
Definition: exp_saturation.c:45
PythonScripting::redirectStdIO
void redirectStdIO() override
Definition: PythonScripting.cpp:283
PYUNICODE_AsUTF8
#define PYUNICODE_AsUTF8
Definition: PythonScripting.cpp:73
initQtCore
void initQtCore()
PythonScripting::~PythonScripting
~PythonScripting()
Definition: PythonScripting.cpp:320
PYUNICODE_FromString
#define PYUNICODE_FromString
Definition: PythonScripting.cpp:74
ApplicationWindow.h
PythonScripting::sys
PyObject * sys
Definition: PythonScripting.h:103
PythonScripting::mathFunctionDoc
const QString mathFunctionDoc(const QString &name) const override
Return a documentation string for the given mathematical function.
Definition: PythonScripting.cpp:452
_traceback::tb_lineno
int tb_lineno
Definition: PythonScripting.cpp:50
PyObject
struct _object PyObject
Definition: PythonScript.h:37
PYTHON_UTIL_PATH
#define PYTHON_UTIL_PATH
Definition: PythonScripting.cpp:328