Salome HOME
6ad2fe9afce71c1b069d1d366e8b55852e8d33c5
[modules/gui.git] / tools / PyInterp / src / PyInterp_Interp.cxx
1 // Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 //  File   : PyInterp_Interp.cxx
23 //  Author : Christian CAREMOLI, Paul RASCLE, Adrien BRUNETON
24
25 #include "PyInterp_Interp.h"  // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
26 #include "PyInterp_Utils.h"
27
28 #include <pythread.h>
29 //#include <cStringIO.h>
30 #include <structmember.h>
31 #include <string>
32 #include <vector>
33 #include <map>
34 #include <iostream>
35 #include <sstream>
36 #include <algorithm>
37
38 #include <QRegularExpression>
39 #include <QStringList>
40
41 #define TOP_HISTORY_PY   "--- top of history ---"
42 #define BEGIN_HISTORY_PY "--- begin of history ---"
43
44 /*
45   The following functions are used to hook the Python
46   interpreter output.
47 */
48
49 static void
50 PyStdOut_dealloc(PyStdOut *self)
51 {
52   PyObject_Del(self);
53 }
54
55 static PyObject*
56 PyStdOut_write(PyStdOut *self, PyObject *args)
57 {
58   char *c;
59   if (!PyArg_ParseTuple(args, "s",&c))
60     return NULL;
61   if(self->_cb==NULL) {
62     if ( self->_iscerr )
63       std::cerr << c ;
64     else
65       std::cout << c ;
66   }
67   else {
68     self->_cb(self->_data,c);
69   }
70   Py_INCREF(Py_None);
71   return Py_None;
72 }
73
74 static PyObject*
75 PyStdOut_flush(PyStdOut * /*self*/, PyObject * /*args*/)
76 {
77   Py_INCREF(Py_None);
78   return Py_None;
79 }
80
81 static PyObject*
82 PyStdOut_isatty(PyStdOut * /*self*/, PyObject */*args*/)
83 {
84   return Py_False;
85 }
86
87 static PyMethodDef PyStdOut_methods[] = {
88   {"write",  (PyCFunction)PyStdOut_write,  METH_VARARGS, PyDoc_STR("write(string) -> None")},
89   {"flush",  (PyCFunction)PyStdOut_flush,  METH_NOARGS,  PyDoc_STR("flush() -> None")},
90   {"isatty", (PyCFunction)PyStdOut_isatty, METH_NOARGS,  PyDoc_STR("isatty() -> bool")},
91   {NULL,     (PyCFunction)NULL,            0,            NULL}   /* sentinel */
92 };
93
94 static PyMemberDef PyStdOut_memberlist[] = {
95   {(char*)"softspace", T_INT,  offsetof(PyStdOut, softspace), 0,
96    (char*)"flag indicating that a space needs to be printed; used by print"},
97   {NULL, 0, 0, 0, NULL} /* Sentinel */
98 };
99
100 static PyTypeObject PyStdOut_Type = {
101   /* The ob_type field must be initialized in the module init function
102    * to be portable to Windows without using C++. */
103   PyVarObject_HEAD_INIT(NULL, 0)
104   /*0,*/                            /*ob_size*/
105   "PyOut",                      /*tp_name*/
106   sizeof(PyStdOut),             /*tp_basicsize*/
107   0,                            /*tp_itemsize*/
108   /* methods */
109   (destructor)PyStdOut_dealloc, /*tp_dealloc*/
110   0,                            /*tp_print*/
111   0,                            /*tp_getattr*/
112   0,                            /*tp_setattr*/
113   0,                            /*tp_compare*/
114   0,                            /*tp_repr*/
115   0,                            /*tp_as_number*/
116   0,                            /*tp_as_sequence*/
117   0,                            /*tp_as_mapping*/
118   0,                            /*tp_hash*/
119   0,                            /*tp_call*/
120   0,                            /*tp_str*/
121   PyObject_GenericGetAttr,      /*tp_getattro*/
122   /* softspace is writable:  we must supply tp_setattro */
123   PyObject_GenericSetAttr,      /* tp_setattro */
124   0,                            /*tp_as_buffer*/
125   Py_TPFLAGS_DEFAULT,           /*tp_flags*/
126   0,                            /*tp_doc*/
127   0,                            /*tp_traverse*/
128   0,                            /*tp_clear*/
129   0,                            /*tp_richcompare*/
130   0,                            /*tp_weaklistoffset*/
131   0,                            /*tp_iter*/
132   0,                            /*tp_iternext*/
133   PyStdOut_methods,             /*tp_methods*/
134   PyStdOut_memberlist,          /*tp_members*/
135   0,                            /*tp_getset*/
136   0,                            /*tp_base*/
137   0,                            /*tp_dict*/
138   0,                            /*tp_descr_get*/
139   0,                            /*tp_descr_set*/
140   0,                            /*tp_dictoffset*/
141   0,                            /*tp_init*/
142   0,                            /*tp_alloc*/
143   0,                            /*tp_new*/
144   0,                            /*tp_free*/
145   0,                            /*tp_is_gc*/
146   0,                            /*tp_bases*/
147   0,                            /*tp_mro*/
148   0,                            /*tp_cache*/
149   0,                            /*tp_subclasses*/
150   0,                            /*tp_weaklist*/
151   0,                            /*tp_del*/
152   0,                            /*tp_version_tag*/
153   0,                            /*tp_finalize*/
154 #if PY_VERSION_HEX >= 0x03080000
155   0,                            /*tp_vectorcall*/
156 #if PY_VERSION_HEX < 0x03090000
157   0,                            /*tp_print*/
158 #endif
159 #endif
160 };
161
162 #define PyStdOut_Check(v)  ((v)->ob_type == &PyStdOut_Type)
163
164 static PyStdOut* newPyStdOut( bool iscerr )
165 {
166   PyStdOut *self;
167   self = PyObject_New(PyStdOut, &PyStdOut_Type);
168   if (self == NULL)
169     return NULL;
170   self->softspace = 0;
171   self->_cb = NULL;
172   self->_iscerr = iscerr;
173   return self;
174 }
175
176 /*!
177   \class PyInterp_Interp
178   \brief Generic embedded Python interpreter.
179 */
180
181 int   PyInterp_Interp::_argc   = 1;
182 char* PyInterp_Interp::_argv[] = {(char*)""};
183
184 /*!
185   \brief Basic constructor.
186
187   After construction the interpreter instance successor classes
188   must call virtual method initialize().
189 */
190 PyInterp_Interp::PyInterp_Interp():
191   _vout(0), _verr(0), _global_context(0), _local_context(0), _initialized(false)
192 {
193 }
194
195 /*!
196   \brief Destructor.
197 */
198 PyInterp_Interp::~PyInterp_Interp()
199 {
200   destroy();
201 }
202
203 /*!
204   \brief Initialize embedded interpreter.
205
206   This method should be called after construction of the interpreter.
207   The method initialize() calls virtuals methods
208   - initPython()  to initialize global Python interpreter
209   - initContext() to initialize interpreter internal context
210   - initRun()     to prepare interpreter for running commands
211   which should be implemented in the successor classes, according to the
212   embedded Python interpreter policy (mono or multi interpreter, etc).
213 */
214 void PyInterp_Interp::initialize()
215 {
216   if ( initialized() )
217     return; // prevent repeating intitialization
218
219   _initialized = true;
220
221   _history.clear();       // start a new list of user's commands
222   _ith = _history.begin();
223
224   initPython();  // This also inits the multi-threading for Python (but w/o acquiring GIL)
225
226   // ---- The rest of the initialisation process is done hodling the GIL
227   PyLockWrapper lck;
228
229   initContext();
230
231   // used to interpret & compile commands - this is really imported here
232   // and only added again (with PyImport_AddModule) later on
233   PyObjWrapper m(PyImport_ImportModule("codeop"));
234   if(!m) {
235     PyErr_Print();
236     return;
237   }
238
239   // Create python objects to capture stdout and stderr
240   _vout=(PyObject*)newPyStdOut( false ); // stdout
241   _verr=(PyObject*)newPyStdOut( true );  // stderr
242
243   // All the initRun outputs are redirected to the standard output (console)
244   initRun();
245 }
246
247 void PyInterp_Interp::destroy()
248 {
249   PyLockWrapper lck;
250   closeContext();
251 }
252
253 /*!
254   \brief Initialize Python interpreter.
255
256   In case if Python is not initialized, it sets program name, initializes the single true Python
257   interpreter, sets program arguments, and initializes threads.
258   Otherwise, does nothing. This is important for light SALOME configuration,
259   as in full SALOME this is done at SalomeApp level.
260   \sa SalomeApp_PyInterp class and main() in SALOME_Session_Server
261  */
262 void PyInterp_Interp::initPython()
263 {
264   if (!Py_IsInitialized()){
265     // Python is not initialized
266     wchar_t **changed_argv = new wchar_t*[_argc]; // Setting arguments
267     for (int i = 0; i < _argc; i++)
268     {
269       changed_argv[i] = Py_DecodeLocale(_argv[i], NULL);
270     }
271
272 #if PY_VERSION_HEX < 0x03080000
273     Py_SetProgramName(changed_argv[0]);
274     Py_Initialize(); // Initialize the interpreter
275     PySys_SetArgv(_argc, changed_argv);
276 #else
277     PyConfig config;
278     PyConfig_InitPythonConfig(&config);
279     PyStatus status = PyConfig_SetString(&config, &config.program_name, changed_argv[0]);
280     status = PyConfig_SetArgv(&config, _argc, changed_argv);
281     status = Py_InitializeFromConfig(&config);
282     PyConfig_Clear(&config);
283 #endif
284
285 #if PY_VERSION_HEX < 0x03070000
286     PyEval_InitThreads(); // Create (and acquire) the Python global interpreter lock (GIL)
287 #endif
288     PyEval_SaveThread(); // release safely GIL
289   }
290 }
291
292 /*!
293   \brief Get embedded Python interpreter banner.
294   \return banner string
295  */
296 std::string PyInterp_Interp::getBanner() const
297 {
298   PyLockWrapper lck;
299   std::string aBanner("Python ");
300   aBanner = aBanner + Py_GetVersion() + " on " + Py_GetPlatform() ;
301   aBanner = aBanner + "\ntype help to get general information on environment\n";
302   return aBanner;
303 }
304
305 /*!
306   \brief Initialize run command.
307
308   This method is used to prepare interpreter for running
309   Python commands.
310
311   \return \c true on success and \c false on error
312 */
313 bool PyInterp_Interp::initRun()
314 {
315   return true;
316 }
317
318 /*!
319  * Initialize context dictionaries. GIL is held already.
320  * The code executed in an embedded interpreter is expected to be run at the module
321  * level, in which case local and global context have to be the same dictionary.
322  * See: http://stackoverflow.com/questions/12265756/c-python-running-python-code-within-a-context
323  * for an explanation.
324  */
325 bool PyInterp_Interp::initContext()
326 {
327   PyObject *m = PyImport_AddModule("__main__");  // interpreter main module (module context)
328   if(!m){
329     PyErr_Print();
330     return false;
331   }
332   _global_context = PyModule_GetDict(m);          // get interpreter global variable context
333   Py_INCREF(_global_context);
334   _local_context = _global_context;
335
336   return true;
337 }
338
339 /*!
340  * Destroy context dictionaries. GIL is held already.
341  */
342 void PyInterp_Interp::closeContext()
343 {
344   Py_XDECREF(_global_context);
345   // both _global_context and _local_context may point to the same Python object
346   if ( _global_context != _local_context)
347     Py_XDECREF(_local_context);
348 }
349
350 /*!
351   \brief Compile Python command and evaluate it in the
352          python dictionary contexts if possible. This is not thread-safe.
353          This is the caller's responsibility to make this thread-safe.
354   \internal
355   \param command Python command string
356   \return -1 on fatal error, 1 if command is incomplete and 0
357          if command is executed successfully
358  */
359 static int run_command(const char *command, PyObject * global_ctxt, PyObject * local_ctxt)
360 {
361   PyObject *m = PyImport_AddModule("codeop");
362   if(!m) {
363     // Fatal error. No way to go on.
364     PyErr_Print();
365     return -1;
366   }
367
368   PyObjWrapper v(PyObject_CallMethod(m,(char*)"compile_command",(char*)"s",command));
369   if(!v) {
370     // Error encountered. It should be SyntaxError,
371     //so we don't write out traceback
372     PyObjWrapper exception, value, tb;
373     PyErr_Fetch(&exception, &value, &tb);
374     PyErr_NormalizeException(&exception, &value, &tb);
375     PyErr_Display(exception, value, NULL);
376     return -1;
377   }
378   else if (v == Py_None) {
379     // Incomplete text we return 1 : we need a complete text to execute
380     return 1;
381   }
382   else {
383     PyObjWrapper r(PyEval_EvalCode((PyObject *)(void *)v,global_ctxt, local_ctxt));
384     if(!r) {
385       // Execution error. We return -1
386       PyErr_Print();
387       return -1;
388     }
389     // The command has been successfully executed. Return 0
390     return 0;
391   }
392 }
393
394 void replaceAll(std::string& str, const std::string& from, const std::string& to) {
395     if(from.empty())
396         return;
397     size_t start_pos = 0;
398     while((start_pos = str.find(from, start_pos)) != std::string::npos) {
399         str.replace(start_pos, from.length(), to);
400         start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
401     }
402 }
403
404 std::vector<std::string>
405 __split(const std::string& str, char delimiter)
406 {
407   std::vector<std::string> internal;
408   std::stringstream ss(str); // Turn the string into a stream.
409   std::string tok;
410
411   while (getline(ss, tok, delimiter)) {
412     internal.push_back(tok);
413   }
414
415   return internal;
416 }
417
418 std::string
419 __join(const std::vector<std::string>& v, int begin=0, int end=-1)
420 {
421   if (end == -1)
422     end = (int)v.size(); //!< TODO: conversion from size_t to int
423   std::stringstream ss;
424   for (int i = begin; i < end; ++i) {
425     if (i != begin)
426       ss << ",";
427     ss << v[i];
428   }
429   return ss.str();
430 }
431
432 std::vector<std::string>
433 __getArgsList(std::string argsString)
434 {
435   // Special process if some items of 'args:' list are themselves lists
436   // Note that an item can be a list, but not a list of lists...
437   // So we can have something like this:
438   // myscript.py args:[\'file1\',\'file2\'],\'val1\',\"done\",[1,2,3],[True,False],\"ok\",kwarg1=\'kwarg1\',kwarg2=\'kwarg2\',\'fin\'
439   // With such a call, argsString variable contains the string representing ['file1','file2'],'val1','done',[1,2,3],[True,False],'ok',kwarg1='kwarg1',kwarg2='kwarg2','fin'
440   // We have to split argsString to obtain a 9 string elements list
441   std::vector<std::string> x = __split(argsString, ',');
442   bool containsList = (argsString.find('[') != std::string::npos);
443   if (containsList) {
444     std::vector<int> listBeginIndices, listEndIndices;
445     for (int pos = 0; pos < (int)x.size(); ++pos) {
446       if (x[pos][0] == '[')
447         listBeginIndices.push_back(pos);
448       else if (x[pos][x[pos].size()-1] == ']')
449         listEndIndices.push_back(pos);
450     }
451     std::vector<std::string> extractedArgs;
452     int start = 0;
453     for (int pos = 0; pos < (int)listBeginIndices.size(); ++pos) {
454       int lbeg = listBeginIndices[pos];
455       int lend = listEndIndices[pos];
456       if (lbeg > start)
457         for (int k = start; k < lbeg; ++k)
458           extractedArgs.push_back(x[k]);
459       extractedArgs.push_back(__join(x, lbeg, lend+1));
460       start = lend+1;
461     }
462     if (start < (int)x.size())
463       for (int k = start; k < (int)x.size(); ++k)
464         extractedArgs.push_back(x[k]);
465     return extractedArgs;
466   }
467   else {
468     return x;
469   }
470 }
471
472 /*!
473   \brief Compile Python command and evaluate it in the
474          python dictionary context if possible. Command might correspond to
475          the execution of a script with optional arguments.
476          In this case, command is:
477            exec(open(r"/absolute/path/to/script.py", "rb").read(), args=(arg1,...,argn))
478          and args parameter is optional one. This parameter is specified as a tuple of strings.
479   \internal
480   \param command Python command string
481   \param context Python context (dictionary)
482   \return -1 on fatal error, 1 if command is incomplete and 0
483          if command is executed successfully
484  */
485 static int compile_command(const char *command, PyObject * global_ctxt, PyObject * local_ctxt)
486 {
487   // First guess if command is execution of a script with args, or a simple Python command
488   QString singleCommand = command;
489   QString commandArgs = "";
490
491   QRegularExpression rx("exec\\s*\\(.*open\\s*\\(\\s*(.*)\\s*\\)\\s*\\.\\s*read\\s*\\(\\)(\\s*,\\s*args\\s*=\\s*\\(.*\\))\\s*\\)");
492   QRegularExpressionMatch match = rx.match(command);
493   if (match.hasMatch()) {
494     commandArgs = match.captured(2).remove(0, match.captured(2).indexOf("(")); // arguments of command
495     commandArgs.insert(commandArgs.indexOf('(')+1, match.captured(1).split(",")[0].trimmed() + ","); // prepend arguments list by the script file itself
496     singleCommand = singleCommand.remove(match.capturedStart(2), match.captured(2).size()); // command for execution without arguments
497   }
498
499   if (commandArgs.isEmpty()) {
500     return run_command(singleCommand.toStdString().c_str(), global_ctxt, local_ctxt);
501   }
502   else {
503     ///////////////std::vector<std::string> argList = __getArgsList(commandArgs);
504     QString preCommandBegin = "import sys; save_argv = sys.argv; sys.argv=list(";
505     QString preCommandEnd = ");";
506     QString postCommand = ";sys.argv=save_argv";
507     QString completeCommand = preCommandBegin+commandArgs+preCommandEnd+singleCommand.trimmed()+postCommand;
508     return run_command(completeCommand.toStdString().c_str(), global_ctxt, local_ctxt);
509   }
510 }
511
512 /*!
513   \brief Run Python command - the command has to fit on a single line (no \n!).
514   Use ';' if you need multiple statements evaluated at once.
515   \param command Python command
516   \return command status
517 */
518 int PyInterp_Interp::run(const char *command)
519 {
520   beforeRun();
521   int ret = simpleRun(command);
522   afterRun();
523   return ret;
524 }
525
526 /**
527  * Called before a command is run (when calling run() method). Not thread-safe. Caller's responsibility
528  * to acquire GIL if needed.
529  */
530 int PyInterp_Interp::beforeRun()
531 {
532   return 0;
533 }
534
535 /**
536  * Called after a command is run (when calling run() method). Not thread-safe. Caller's responsibility
537  * to acquire GIL if needed.
538  */
539 int PyInterp_Interp::afterRun()
540 {
541   return 0;
542 }
543
544 /*!
545   \brief Run Python command (used internally). Not thread-safe. GIL acquisition is caller's responsibility.
546   \param command Python command
547   \param addToHistory if \c true (default), the command is added to the commands history
548   \return command status
549 */
550 int PyInterp_Interp::simpleRun(const char *command, const bool addToHistory)
551 {
552   if( addToHistory && strcmp(command,"") != 0 ) {
553     _history.push_back(command);
554     _ith = _history.end();
555   }
556
557   // Current stdout and stderr are saved
558   PyObject * oldOut = PySys_GetObject((char*)"stdout");
559   PyObject * oldErr = PySys_GetObject((char*)"stderr");
560   // Keep them alive (PySys_GetObject returned a *borrowed* ref!)
561   Py_INCREF(oldOut);
562   Py_INCREF(oldErr);
563
564   // Redirect outputs to SALOME Python console before treatment
565   PySys_SetObject((char*)"stderr",_verr);
566   PySys_SetObject((char*)"stdout",_vout);
567
568   int ier = compile_command(command, _global_context, _local_context);
569
570   // Outputs are redirected to what they were before
571   PySys_SetObject((char*)"stdout",oldOut);
572   PySys_SetObject((char*)"stderr",oldErr);
573
574   return ier;
575 }
576
577 /*!
578   \brief Get previous command in the commands history.
579   \return previous command
580 */
581 const char * PyInterp_Interp::getPrevious()
582 {
583   if(_ith != _history.begin()){
584     _ith--;
585     return (*_ith).c_str();
586   }
587   else
588     return BEGIN_HISTORY_PY;
589 }
590
591 /*!
592   \brief Get next command in the commands history.
593   \return next command
594 */
595 const char * PyInterp_Interp::getNext()
596 {
597   if(_ith != _history.end()){
598     _ith++;
599   }
600   if (_ith == _history.end())
601     return TOP_HISTORY_PY;
602   else
603     return (*_ith).c_str();
604 }
605
606 /*!
607   \brief Set Python standard output device hook.
608   \param cb callback function
609   \param data callback function parameters
610 */
611 void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data)
612 {
613   ((PyStdOut*)_vout)->_cb=cb;
614   ((PyStdOut*)_vout)->_data=data;
615 }
616
617 /*!
618   \brief Set Python standard error device hook.
619   \param cb callback function
620   \param data callback function parameters
621 */
622 void PyInterp_Interp::setverrcb(PyOutChanged* cb, void* data)
623 {
624   ((PyStdOut*)_verr)->_cb=cb;
625   ((PyStdOut*)_verr)->_data=data;
626 }
627
628 /*!
629   \bried Check if the interpreter is initialized
630   \internal
631 */
632 bool PyInterp_Interp::initialized() const
633 {
634   return _initialized;
635 }