itom  3.0.0
pythonEngine.h
1 /* ********************************************************************
2  itom software
3  URL: http://www.uni-stuttgart.de/ito
4  Copyright (C) 2016, Institut fuer Technische Optik (ITO),
5  Universitaet Stuttgart, Germany
6 
7  This file is part of itom.
8 
9  itom is free software; you can redistribute it and/or modify it
10  under the terms of the GNU Library General Public Licence as published by
11  the Free Software Foundation; either version 2 of the Licence, or (at
12  your option) any later version.
13 
14  itom is distributed in the hope that it will be useful, but
15  WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
17  General Public Licence for more details.
18 
19  You should have received a copy of the GNU Library General Public License
20  along with itom. If not, see <http://www.gnu.org/licenses/>.
21 *********************************************************************** */
22 
23 #ifndef PYTHONENGINE_H
24 #define PYTHONENGINE_H
25 
26 /*if you add any include to this file you will DIE an immediate, horrible, painful death*/
27 
28 #include <string>
29 #include <exception>
30 //#ifndef Q_MOC_RUN
31 // #define PY_ARRAY_UNIQUE_SYMBOL itom_ARRAY_API
32 // #define NO_IMPORT_ARRAY
33 //#endif
34 
35 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION //see comment in pythonNpDataObject.cpp
36 
37 #ifndef Q_MOC_RUN
38  //python
39  // see http://vtk.org/gitweb?p=VTK.git;a=commitdiff;h=7f3f750596a105d48ea84ebfe1b1c4ca03e0bab3
40  #if (defined _DEBUG) && (defined WIN32)
41  #undef _DEBUG
42  #if (defined linux) | (defined CMAKE)
43  #include "Python.h"
44  #include "node.h"
45  #include "numpy/arrayobject.h"
46  #elif (defined __APPLE__) | (defined CMAKE)
47  #include "Python.h"
48  #include "node.h"
49  #include "numpy/arrayobject.h"
50  #else
51  #include "Python.h"
52  #include "node.h"
53  #include "../Lib/site-packages/numpy/core/include/numpy/arrayobject.h" //for numpy arrays
54  #endif
55  #define _DEBUG
56  #else
57  #ifdef linux
58  #include "Python.h"
59  #include "node.h"
60  #include "numpy/arrayobject.h"
61  #elif (defined __APPLE__)
62  #include "Python.h"
63  #include "node.h"
64  #include "numpy/arrayobject.h"
65  #else
66  #include "Python.h"
67  #include "node.h"
68  #include "../Lib/site-packages/numpy/core/include/numpy/arrayobject.h" //for numpy arrays
69  #endif
70  #endif
71 #endif // Q_MOC_RUN
72 
73 /* includes */
74 
75 #include "pythonNpDataObject.h"
76 #include "pythonItom.h"
77 #include "pythonProxy.h"
78 #include "pythonStream.h"
79 
80 #include "../models/breakPointModel.h"
81 #include "../../common/sharedStructuresQt.h"
82 #include "../../common/addInInterface.h"
83 #include "../global.h"
84 
85 #include "pythonWorkspace.h"
86 
87 #include <qstringlist.h>
88 #include <qqueue.h>
89 #include <qset.h>
90 #include <qpointer.h>
91 #include <qatomic.h>
92 
93 
94 /* definition and macros */
95 
96 /* global variables (avoid) */
97 
98 /* content */
99 
100 class QDesktopWidget;
101 class QTimer;
102 
103 namespace ito
104 {
105 
107 {
108 public:
109  FuncWeakRef();
110  FuncWeakRef(PythonProxy::PyProxy *proxyObject, PyObject *argTuple = NULL);
111  FuncWeakRef(const FuncWeakRef &rhs);
112  ~FuncWeakRef();
113  FuncWeakRef& operator =(FuncWeakRef rhs);
114 
115  PythonProxy::PyProxy* getProxyObject() const { return m_proxyObject; } //borrowed reference
116  PyObject* getArguments() const { return m_argument; } //borrowed reference
117  bool isValid() const { return (m_proxyObject != NULL); }
118 
119  void setHandle(const size_t &handle);
120  size_t getHandle() const { return m_handle; }
121 private:
122  PythonProxy::PyProxy *m_proxyObject;
123  PyObject *m_argument;
124  size_t m_handle;
125 };
126 
127 
128 class PythonEngine : public QObject
129 {
130  Q_OBJECT
131 
132 public:
133  PythonEngine(); //constructor
134  ~PythonEngine(); //destructor
135 
136  Q_INVOKABLE void pythonSetup(ito::RetVal *retValue); //setup
137  Q_INVOKABLE ito::RetVal scanAndRunAutostartFolder(QString currentDirAfterScan = QString() );
138  Q_INVOKABLE ito::RetVal pythonShutdown(ItomSharedSemaphore *aimWait = NULL); //shutdown
139  Q_INVOKABLE ito::RetVal stringEncodingChanged();
140 
141  inline ito::BreakPointModel *getBreakPointModel() const { return bpModel; }
142  inline bool isPythonBusy() const { return pythonState != ito::pyStateIdle; }
143  inline bool isPythonDebugging() const { return (pythonState == ito::pyStateDebuggingWaitingButBusy || pythonState == ito::pyStateDebugging || pythonState == ito::pyStateDebuggingWaiting); }
144  inline bool isPythonDebuggingAndWaiting() const { return pythonState == ito::pyStateDebuggingWaiting; }
145  inline bool execInternalCodeByDebugger() const { return m_executeInternalPythonCodeInDebugMode; }
146  inline void setExecInternalCodeByDebugger(bool value) { m_executeInternalPythonCodeInDebugMode = value; }
147  ito::RetVal checkForPyExceptions();
148  void printPythonErrorWithoutTraceback();
149  void pythonDebugFunction(PyObject *callable, PyObject *argTuple, bool gilExternal = false);
150  void pythonRunFunction(PyObject *callable, PyObject *argTuple, bool gilExternal = false);
151  inline PyObject *getGlobalDictionary() const { return globalDictionary; }
152  inline bool pySyntaxCheckAvailable() const { return (m_pyModSyntaxCheck != NULL); }
153  QList<int> parseAndSplitCommandInMainComponents(const char *str, QByteArray &encoding) const; //can be directly called from different thread
154  QString getPythonExecutable() const { return m_pythonExecutable; }
155  Qt::HANDLE getPythonThreadId() const { return m_pythonThreadId; }
156 
157  static bool isInterruptQueued();
158  static const PythonEngine *getInstance();
159 protected:
160  //RetVal syntaxCheck(char* pythonFileName); // syntaxCheck for file with filename pythonFileName
161  ito::RetVal runPyFile(const QString &pythonFileName); // run file pythonFileName
162  ito::RetVal debugFile(const QString &pythonFileName); // debug file pythonFileName
163  ito::RetVal runString(const QString &command); // run string command
164  ito::RetVal debugString(const QString &command); // debug string command
165  ito::RetVal debugFunction(PyObject *callable, PyObject *argTuple, bool gilExternal = false);
166  ito::RetVal runFunction(PyObject *callable, PyObject *argTuple, bool gilExternal = false);
167 
168  ito::RetVal modifyTracebackDepth(int NrOfLevelsToPopAtFront = -1, bool showTraceback = true);
169 
170  PyObject* setPyErrFromException(const std::exception &exc);
171 
172 #if QT_VERSION >= 0x050000
173  void connectNotify(const QMetaMethod &signal);
174 #else
175  void connectNotify(const char* signal);
176 #endif
177 
178 private:
179  static PythonEngine *getInstanceInternal();
180 
181  inline PyObject *getLocalDictionary() { return localDictionary; }
183  PyObject *getPyObjectByFullName(bool globalNotLocal, const QStringList &fullNameSplittedByDelimiter, QString *validVariableName = NULL); //Python GIL must be locked when calling this function!
184  PyObject *getPyObjectByFullName(bool globalNotLocal, const QString &fullName, QString *validVariableName = NULL); //Python GIL must be locked when calling this function!
185 
186  void setGlobalDictionary(PyObject* mainDict = NULL);
187  void setLocalDictionary(PyObject* localDict);
188 
189  void emitPythonDictionary(bool emitGlobal, bool emitLocal, PyObject* globalDict, PyObject* localDict);
190 
191  ito::RetVal pickleDictionary(PyObject *dict, const QString &filename);
192  ito::RetVal unpickleDictionary(PyObject *destinationDict, const QString &filename, bool overwrite);
193 
194  //methods for debugging
195  void enqueueDbgCmd(ito::tPythonDbgCmd dbgCmd);
196  ito::tPythonDbgCmd dequeueDbgCmd();
197  bool DbgCommandsAvailable();
198  void clearDbgCmdLoop();
199 
200  ito::RetVal pythonStateTransition(tPythonTransitions transition);
201 
202  //methods for breakpoint
203  ito::RetVal pythonAddBreakpoint(const QString &filename, const int lineno, const bool enabled, const bool temporary, const QString &condition, const int ignoreCount, int &pyBpNumber);
204  ito::RetVal pythonEditBreakpoint(const int pyBpNumber, const QString &filename, const int lineno, const bool enabled, const bool temporary, const QString &condition, const int ignoreCount);
205  ito::RetVal pythonDeleteBreakpoint(const int pyBpNumber);
206 
207  ito::RetVal autoReloaderCheck();
208 
209  static int queuedInterrupt(void *state);
210 
211  PyObject* getAndCheckIdentifier(const QString &identifier, ito::RetVal &retval) const;
212 
213 
214  //member variables
215  bool m_started;
216  //QString m_itomMemberClasses;
217 
218  //PyGILState_STATE threadState;
219 
220  QMutex dbgCmdMutex;
221  QMutex pythonStateChangeMutex;
222  QMutex dictChangeMutex;
223  QDesktopWidget *m_pDesktopWidget;
224  QQueue<ito::tPythonDbgCmd> debugCommandQueue;
225  ito::tPythonDbgCmd debugCommand;
226 
227  ito::tPythonState pythonState;
228 
229  ito::BreakPointModel *bpModel;
230 
231  PyObject* mainModule;
232  PyObject* mainDictionary;
233  PyObject* localDictionary;
234  PyObject* globalDictionary;
235  PyObject *itomDbgModule;
236  PyObject *itomDbgInstance;
237  PyObject *itomModule;
238  PyObject *itomFunctions;
239  PyObject *m_pyModGC;
240  PyObject *m_pyModSyntaxCheck;
241  //PyObject *itomReturnException; //!< if this exception is thrown, the execution of the main application is stopped
242 
243  Qt::HANDLE m_pythonThreadId;
244 
245  PyObject *dictUnicode;
246 
247  QSet<ito::PyWorkspaceContainer*> m_mainWorkspaceContainer;
248  QSet<ito::PyWorkspaceContainer*> m_localWorkspaceContainer;
249  QHash<size_t, FuncWeakRef> m_pyFuncWeakRefHashes;
250  size_t m_pyFuncWeakRefAutoInc;
251 
253 
255  PyMethodDef* PythonAdditionalModuleITOM;
256 
257  // decides if itom is automatically included in every source file before it is handed to the syntax checker
258  bool m_includeItomImportBeforeSyntaxCheck;
260 
261  wchar_t *m_pUserDefinedPythonHome;
262 
263  struct AutoReload
264  {
265  PyObject *modAutoReload;
266  PyObject *classAutoReload;
267  bool enabled;
268  bool checkFileExec;
269  bool checkStringExec;
270  bool checkFctExec;
271  };
272 
274 
276  static PyMethodDef PyMethodItomDbg[];
277  static PyModuleDef PyModuleItomDbg;
278  static PyObject* PyInitItomDbg(void);
279  static PyObject* PyDbgCommandLoop(PyObject *pSelf, PyObject *pArgs);
280 
281  //helper methods
282  //static PyObject* checkForTimeoutHelper(ItomSharedSemaphore* semaphore, int timeout, PyObject *retValueOk);
283 
284  //other static members
285  static QMutex instatiated;
286  static QMutex instancePtrProtection;
287  static QString fctHashPrefix;
288 
289  static PythonEngine* instance;
290 
291  QAtomicInt m_interruptCounter; //protects that a python interrupt can only be placed if there is no interrupt event queued yet.
292 
293  // friend class
294  friend class ito::PythonItom;
295  friend class ito::PyStream;
296 
297 signals:
298  void pythonDebugPositionChanged(QString filename, int lineNo);
299  void pythonStateChanged(tPythonTransitions pyTransition);
300  void pythonModifyLocalDict(PyObject* localDict, ItomSharedSemaphore* semaphore);
301  void pythonModifyGlobalDict(PyObject* globalDict, ItomSharedSemaphore* semaphore);
302  void pythonCurrentDirChanged();
303  void updateCallStack(QStringList filenames, IntList lines, QStringList methods);
304  void deleteCallStack();
305 
306  void pythonSetCursor(const Qt::CursorShape cursor);
307  void pythonResetCursor();
308  void pythonAutoReloadChanged(bool enabled, bool checkFile, bool checkCmd, bool checkFct);
309  void clearCommandLine();
310  void startInputCommandLine(QSharedPointer<QByteArray> buffer, ItomSharedSemaphore *semaphore);
311 
312 public slots:
313  void pythonRunString(QString cmd);
314  void pythonDebugString(QString cmd);
315  void pythonExecStringFromCommandLine(QString cmd);
316  void pythonRunFile(QString filename);
317  void pythonDebugFile(QString filename);
318  void pythonRunStringOrFunction(QString cmdOrFctHash);
319  void pythonDebugStringOrFunction(QString cmdOrFctHash);
320  void pythonInterruptExecution();
321  void pythonDebugCommand(tPythonDbgCmd cmd);
322 
323  void setAutoReloader(bool enabled, bool checkFile, bool checkCmd, bool checkFct);
324 
325  // Settings are neccesary for automatic itom inclusion and syntax check
326  void readSettings();
327  void propertiesChanged();
328 
329  void pythonSyntaxCheck(const QString &code, QPointer<QObject> sender);
330 
331  void pythonGenericSlot(PyObject* callable, PyObject *argumentTuple);
332 
334  void breakPointAdded(BreakPointItem bp, int row);
335  void breakPointDeleted(QString filename, int lineNo, int pyBpNumber);
336  void breakPointChanged(BreakPointItem oldBp, BreakPointItem newBp);
337  ito::RetVal setupBreakPointDebugConnections();
338  ito::RetVal shutdownBreakPointDebugConnections();
339 
340  bool renameVariable(bool globalNotLocal, const QString &oldFullItemName, QString newKey, ItomSharedSemaphore *semaphore = NULL);
341  bool deleteVariable(bool globalNotLocal, const QStringList &fullItemNames, ItomSharedSemaphore *semaphore = NULL);
342  ito::RetVal pickleVariables(bool globalNotLocal, QString filename, QStringList varNames, ItomSharedSemaphore *semaphore = NULL);
343  ito::RetVal pickleSingleParam(QString filename, QSharedPointer<ito::Param> value, const QString &valueName, ItomSharedSemaphore *semaphore = NULL);
344  ito::RetVal unpickleVariables(bool globalNotLocal, QString filename, QString packedVarName, ItomSharedSemaphore *semaphore = NULL);
345  ito::RetVal saveMatlabVariables(bool globalNotLocal, QString filename, QStringList varNames, ItomSharedSemaphore *semaphore = NULL);
346  ito::RetVal saveMatlabSingleParam(QString filename, QSharedPointer<ito::Param> value, const QString &valueName, ItomSharedSemaphore *semaphore = NULL);
347  ito::RetVal loadMatlabVariables(bool globalNotLocal, QString filename, QString packedVarName, ItomSharedSemaphore *semaphore = NULL);
348  ito::RetVal registerAddInInstance(QString varname, ito::AddInBase *instance, ItomSharedSemaphore *semaphore = NULL);
349  ito::RetVal getSysModules(QSharedPointer<QStringList> modNames, QSharedPointer<QStringList> modFilenames, QSharedPointer<IntList> modTypes, ItomSharedSemaphore *semaphore = NULL);
350  ito::RetVal reloadSysModules(QSharedPointer<QStringList> modNames, ItomSharedSemaphore *semaphore = NULL);
351 
352  void registerWorkspaceContainer(PyWorkspaceContainer *container, bool registerNotUnregister, bool globalNotLocal);
353  void workspaceGetChildNode(PyWorkspaceContainer *container, QString fullNameParentItem);
354  void workspaceGetValueInformation(PyWorkspaceContainer *container, const QString &fullItemName, QSharedPointer<QString> extendedValue, ItomSharedSemaphore *semaphore = NULL);
355 
356  ito::RetVal checkVarnamesInWorkspace(bool globalNotLocal, const QStringList &names, QSharedPointer<IntList> existing, ItomSharedSemaphore *semaphore = NULL);
357  ito::RetVal putParamsToWorkspace(bool globalNotLocal, const QStringList &names, const QVector<SharedParamBasePointer > &values, ItomSharedSemaphore *semaphore = NULL);
358  ito::RetVal getVarnamesListInWorkspace(bool globalNotLocal, const QString &find, QSharedPointer<QStringList> varnameList, ItomSharedSemaphore *semaphore /*= NULL*/);
359  ito::RetVal getParamsFromWorkspace(bool globalNotLocal, const QStringList &names, QVector<int> paramBaseTypes, QSharedPointer<SharedParamBasePointerVector > values, ItomSharedSemaphore *semaphore = NULL);
360 
361 private slots:
362 
363 };
364 
365 } //end namespace ito
366 
367 
368 #endif
Definition: pythonItom.h:52
Definition: pythonEngine.h:128
QHash< size_t, FuncWeakRef > m_pyFuncWeakRefHashes
hash table containing weak reference to callable python methods or functions and as second...
Definition: pythonEngine.h:249
PyObject * getGlobalDictionary() const
Definition: pythonEngine.h:151
PyObject * itomDbgModule
debugger module
Definition: pythonEngine.h:235
PyObject * mainDictionary
main dictionary of python [borrowed]
Definition: pythonEngine.h:232
Class for managing status values (like errors or warning)
Definition: retVal.h:54
Base class for all plugins.
Definition: addInInterface.h:356
Definition: pythonProxy.h:48
Definition: apiFunctionsGraph.cpp:39
Definition: pythonEngine.h:263
semaphore which can be used for asychronous thread communication. By using this class it is possible ...
Definition: sharedStructuresQt.h:57
PyObject * itomDbgInstance
debugger instance
Definition: pythonEngine.h:236
static class which implements a new python type. The members cout and cerr of the python system are s...
Definition: pythonStream.h:45
PyObject * itomFunctions
ito functions [additional python methods] [new ref]
Definition: pythonEngine.h:238
PyObject * getLocalDictionary()
Definition: pythonEngine.h:181
bool m_executeInternalPythonCodeInDebugMode
if true, button events, user interface connections to python methods... will be executed by debugger ...
Definition: pythonEngine.h:254
PyObject * localDictionary
local dictionary of python [borrowed], usually NULL unless if debugger is in "interaction-mode", then globalDictionary is equal to the local dictionary of the current frame
Definition: pythonEngine.h:233
PyObject * itomModule
itom module [new ref]
Definition: pythonEngine.h:237
Definition: pythonEngine.h:106
QString m_includeItomImportString
string that is prepended to each script before syntax check (if m_includeItomImportBeforeSyntaxCheck ...
Definition: pythonEngine.h:259
QString m_pythonExecutable
absolute path to the python executable
Definition: pythonEngine.h:252
PyObject * globalDictionary
global dictionary of python [borrowed], equals to mainDictionary unless if debugger is in "interactio...
Definition: pythonEngine.h:234
Definition: pythonWorkspace.h:115
PyObject * mainModule
main module of python (builtin) [borrowed]
Definition: pythonEngine.h:231
AutoReload m_autoReload
debugger functionality
Definition: pythonEngine.h:273
item of BreakPointModel
Definition: breakPointModel.h:43