]> SALOME platform Git repositories - modules/gui.git/commitdiff
Salome HOME
Prepare Python console to work in async mode with PARAVIS:
authorabn <adrien.bruneton@cea.fr>
Tue, 14 Oct 2014 14:22:11 +0000 (16:22 +0200)
committerabn <adrien.bruneton@cea.fr>
Tue, 14 Oct 2014 14:22:11 +0000 (16:22 +0200)
* exposed Python import lock access in PyLockWrapper
* now also writing stdout/stderr to terminal
* added context in run functions
* added function to post Python in SALOME's main thread

12 files changed:
src/PyConsole/PyConsole_Editor.cxx
src/PyInterp/PyInterp_Interp.cxx
src/PyInterp/PyInterp_Interp.h
src/PyInterp/PyInterp_Utils.cxx
src/PyInterp/PyInterp_Utils.h
src/SALOME_PYQT/SalomePyQt/SalomePyQt.cxx
src/SALOME_PYQT/SalomePyQt/SalomePyQt.h
src/SALOME_PYQT/SalomePyQt/SalomePyQt.sip
src/SUITApp/CMakeLists.txt
src/SUITApp/SUITApp_init_python.cxx
src/SalomeApp/SalomeApp_PyInterp.cxx
src/SalomeApp/SalomeApp_PyInterp.h

index 66aaf5b9b72b12f3e4cf30ad04dbc512a2cfa989..300915c70af1ed2098ebefdb08b1874f5b7db456 100644 (file)
@@ -169,7 +169,7 @@ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp,
   myCmdInHistory( -1 ),
   myEventLoop( 0 ),
   myShowBanner( true ),
-  myIsSync( true ),
+  myIsSync( false ),
   myIsSuppressOutput( false )
 {
   QString fntSet( "" );
index df95ecfb526fe29867303ea2b6ddd2e3eca30fa4..edc89da594e73a8a60558c3cba46976dedd2ad84 100644 (file)
@@ -58,15 +58,16 @@ PyStdOut_write(PyStdOut *self, PyObject *args)
   int l;
   if (!PyArg_ParseTuple(args, "t#:write",&c, &l))
     return NULL;
-  if(self->_cb==NULL) {
-    if ( self->_iscerr )
-      std::cerr << c ;
-    else
-      std::cout << c ;
-  }
-  else {
+
+  // [ABN]: changed this to have Python output on both the Python console
+  // and the terminal (helps to degug the multi-thread)
+  if ( self->_iscerr )
+    std::cerr << c ;
+  else
+    std::cout << c ;
+  if(self->_cb != NULL)
     self->_cb(self->_data,c);
-  }
+
   Py_INCREF(Py_None);
   return Py_None;
 }
@@ -168,10 +169,7 @@ char* PyInterp_Interp::_argv[] = {(char*)""};
 */
 PyInterp_Interp::PyInterp_Interp():
   _vout(0), _verr(0), _local_context(0), _global_context(0)
-{
-}
-
-
+{}
 
 /*!
   \brief Destructor.
@@ -199,8 +197,6 @@ void PyInterp_Interp::initialize()
 
   initPython();  // This also inits the multi-threading for Python (but w/o acquiring GIL)
 
-  //initState(); // [ABN] OBSOLETE
-
   // ---- The rest of the initialisation process is done hodling the GIL
   PyLockWrapper lck;
 
@@ -244,10 +240,10 @@ void PyInterp_Interp::initPython()
     Py_SetProgramName(_argv[0]);
     Py_Initialize(); // Initialize the interpreter
     PySys_SetArgv(_argc, _argv);
-
     PyEval_InitThreads(); // Create (and acquire) the Python global interpreter lock (GIL)
     PyEval_ReleaseLock();
   }
+  PyLockWrapper::Initialize();
 }
 
 /*!
@@ -409,13 +405,15 @@ static int compile_command(const char *command, PyObject * global_ctxt, PyObject
 /*!
   \brief Run Python command - the command has to fit on a single line (no \n!).
   Use ';' if you need multiple statements evaluated at once.
+  The context dictionaries can also be passed (like for the Python 'exec' statement).
+  If not provided it will default to the current interpreter's context.
   \param command Python command
   \return command status
 */
-int PyInterp_Interp::run(const char *command)
+int PyInterp_Interp::run(const char *command, PyObject * globals, PyObject * locals)
 {
   beforeRun();
-  int ret = simpleRun(command);
+  int ret = simpleRun(command, false, globals, locals);
   afterRun();
   return ret;
 }
@@ -444,7 +442,8 @@ int PyInterp_Interp::afterRun()
   \param addToHistory if \c true (default), the command is added to the commands history
   \return command status
 */
-int PyInterp_Interp::simpleRun(const char *command, const bool addToHistory)
+int PyInterp_Interp::simpleRun(const char *command, const bool addToHistory,
+                               PyObject * globals, PyObject * locals)
 {
   if( addToHistory && strcmp(command,"") != 0 ) {
     _history.push_back(command);
@@ -462,7 +461,11 @@ int PyInterp_Interp::simpleRun(const char *command, const bool addToHistory)
   PySys_SetObject((char*)"stderr",_verr);
   PySys_SetObject((char*)"stdout",_vout);
 
-  int ier = compile_command(command, _global_context, _local_context);
+  int ier;
+  if (!globals || !locals)
+    ier = compile_command(command, _global_context, _local_context);
+  else
+    ier = compile_command(command, globals, locals);
 
   // Outputs are redirected to what they were before
   PySys_SetObject((char*)"stdout",oldOut);
index 7883cd4ec99dccdd1e628f6d0b4dbd857db06eb6..bff27815efef1435f8fbb0a8d6d3cb70d17fe9ae 100644 (file)
@@ -64,12 +64,9 @@ public:
   void initialize();
   void destroy();
 
-  virtual int run(const char *command); 
+  virtual int run(const char *command, PyObject * globals=NULL, PyObject * locals=NULL);
   virtual void initStudy(){};
 
-  // [ABN] - the PyLockWrapper is no more attached to the interpreter
-  // PyLockWrapper GetLockWrapper() const;
-
   std::string getbanner() const;
   void setverrcb(PyOutChanged*,void*);
   void setvoutcb(PyOutChanged*,void*);
@@ -90,7 +87,7 @@ protected:
 
   virtual int beforeRun();
   virtual int afterRun();
-  int simpleRun(const char* command, const bool addToHistory = true);
+  int simpleRun(const char* command, const bool addToHistory=true, PyObject * globals=NULL, PyObject * locals=NULL);
 
   virtual void initPython();
 
index 4fa3bc2ae7247c36e8f44a7089a6d55f066213e6..ae7361daaa5990e4fc2566ef77397a726eca9e97 100644 (file)
 //
 
 #include "PyInterp_Utils.h"
+#include <SALOME_Event.h>
+#include <Utils_SALOME_Exception.hxx>
+#include <Container_init_python.hxx>
+#include <utilities.h>
+#include <iostream>
 
 /*!
   \class PyLockWrapper
   \brief Python GIL wrapper.
 */
 
+PyObjWrapper PyLockWrapper::_imp_module(NULL);
+
 /*!
   \brief Constructor. Automatically acquires GIL.
 */
@@ -37,6 +44,58 @@ PyLockWrapper::PyLockWrapper()
   _gil_state = PyGILState_Ensure();
 }
 
+bool PyLockWrapper::Initialize()
+{
+  if (!_imp_module.get())
+    {
+      PyLockWrapper lock;
+      PyObjWrapper m2(PyImport_ImportModule("imp"));
+      _imp_module = m2;
+      if(!_imp_module)
+        {
+          PyErr_Print();
+          return false;
+        }
+    }
+  return true;
+}
+
+bool PyLockWrapper::AcquireImportLock()
+{
+  if (!_imp_module)
+    return false;
+
+  PyLockWrapper lock;
+  PyObject_CallMethod(_imp_module, (char *)"acquire_lock", NULL);
+  if (PyErr_Occurred())
+    {
+      PyErr_Print();
+      return false;
+    }
+  return true;
+}
+
+bool PyLockWrapper::ReleaseImportLockIfLocked()
+{
+  if (!_imp_module)
+    return false;
+  bool ret = false;
+
+  PyLockWrapper lock;
+  PyObjWrapper m(PyObject_CallMethod(_imp_module, (char *)"lock_held", NULL));
+  if (PyObject_IsTrue(m))
+    {
+      PyObject_CallMethod(_imp_module, (char *)"release_lock", NULL);
+      if (PyErr_Occurred())
+        {
+          PyErr_Print();
+          return false;
+        }
+      ret = true;
+    }
+
+  return ret;
+}
 /*!
   \brief Destructor. Automatically releases GIL.
 */
index c9866bab14166088810891a22172ccc0d46701ed..81f64163ff7e62b0fbd0e3d4c23cab264db35361 100644 (file)
 
 #include "PyInterp.h"
 #include <Python.h>
+#include <QMap>
 
-/**
- * Utility class wrapping the Python GIL acquisition. This makes use of the high level
- * API (PyGILState_Ensure and PyGILState_Release), and is hence compatible with only
- * one running Python interpreter (no call to Py_NewInterpreter()).
- * When the class is instanciated the lock is acquired. It is released at destruction time.
- * Copy construction (and hence assignation) is forbidden.
- */
-class PYINTERP_EXPORT PyLockWrapper
-{
-  PyGILState_STATE _gil_state;
-public:
-  PyLockWrapper();
-  ~PyLockWrapper();
-
-private:
-  // "Rule of 3" - Forbid usage of copy operator and copy-constructor
-  PyLockWrapper(const PyLockWrapper & another);
-  const PyLockWrapper & operator=(const PyLockWrapper & another);
-};
-
+#include <QMutex>
 
 /**
  * Utility class to properly handle the reference counting required on Python objects.
@@ -74,5 +56,43 @@ public:
   }
 };
 
+/**
+ * Utility class wrapping the Python GIL acquisition. This makes use of the high level
+ * API (PyGILState_Ensure and PyGILState_Release), and is hence compatible with only
+ * one running Python interpreter (no call to Py_NewInterpreter()).
+ * When the class is instanciated the lock is acquired. It is released at destruction time.
+ * Copy construction (and hence assignation) is forbidden.
+ */
+class PYINTERP_EXPORT PyLockWrapper
+{
+private:
+  PyGILState_STATE _gil_state;
+
+public:
+  PyLockWrapper();
+  ~PyLockWrapper();
+
+  static bool Initialize();
+
+  /* Import lock management - see the API of the imp module:
+   * https://docs.python.org/2/library/imp.html?highlight=imp%20module#module-imp
+   *
+   * This is mainly needed by PARAVIS/ParaView mechanisms - see function createView() and
+   * also executePythonInMainThread() in SalomePyQt.cxx
+   */
+
+  //! Acquire the import lock
+  static bool AcquireImportLock();
+  //! Release the Python import lock if it is held (and return true). Otherwise return false.
+  static bool ReleaseImportLockIfLocked();
+private:
+  // "Rule of 3" - Forbid usage of copy operator and copy-constructor
+  PyLockWrapper(const PyLockWrapper & another);
+  const PyLockWrapper & operator=(const PyLockWrapper & another);
+
+  static PyObjWrapper _imp_module;
+};
+
+
 #endif
 
index 22099f8e4f2e951baef141de383a00394294f8f5..cffbdcb05b9c1e15e82badde9148ca69aaa06290 100644 (file)
@@ -52,6 +52,7 @@
 #include "SUIT_ResourceMgr.h"
 #include "SUIT_Session.h"
 #include "SUIT_Tools.h"
+#include "PyConsole_Interp.h"
 #include "PyConsole_Console.h"
 
 #include <QAction>
@@ -2657,7 +2658,14 @@ public:
 };
 int SalomePyQt::createView( const QString& type, bool visible, const int width, const int height )
 {
-  return ProcessEvent( new TCreateView( type, visible, width, height ) );
+  bool impLock = PyLockWrapper::ReleaseImportLockIfLocked();
+  int ret = ProcessEvent( new TCreateView( type, visible, width, height ) );
+
+  // restore the state of the import lock
+  if (impLock)
+    PyLockWrapper::AcquireImportLock();
+
+  return ret;
 }
 
 /*!
@@ -3829,21 +3837,45 @@ void SalomePyQt::setPlot2dFitRange(const int id, const double XMin, const double
        ProcessVoidEvent( new TPlot2dFitRange(id, XMin, XMax, YMin, YMax) ); 
 }
 
-//class TInitParaview: public SALOME_Event
-//{
-//public:
-//  TInitParaview() {}
-//  virtual void Execute() {
-//    LightApp_Application* anApp = getApplication();
-//    // Create PVViewer_ViewManager, which will initialize ParaView stuff
-//    PVViewer_ViewManager* viewMgr =
-//          dynamic_cast<PVViewer_ViewManager*>( anApp->getViewManager( PVViewer_Viewer::Type(), true ) );
-//  }
-//};
-//void SalomePyQt::initializeParaViewGUI()
-//{
-//  ProcessVoidEvent( new TInitParaview() );
-//}
+/*!
+ * Execute a Python command in the embedded console context, and in the main thread.
+ */
+class TExecutePython: public SALOME_Event
+{
+public:
+  QString myCmd;
+  PyObject * myGlobals;
+  PyObject * myLocals;
+  TExecutePython(const QString& cmd, PyObject * globals, PyObject * locals):
+    myGlobals(globals),
+    myLocals(locals)
+   {
+    myCmd = cmd;
+   }
+  virtual void Execute() {
+    // Get console interpreter
+    LightApp_Application* anApp = getApplication();
+    PyConsole_Console * console = anApp->pythonConsole(false);
+    if (console)
+      {
+        PyLockWrapper lock;
+        console->getInterp()->run( myCmd.toUtf8().data(), myGlobals, myLocals );
+      }
+  }
+};
+
+/**!
+ * Execute Python code in SALOME's main thread.
+ * This is very similar to the native "exec" statement of Python, with the specification
+ * of an execution context.
+ */
+void SalomePyQt::executePythonInMainThread(const QString& cmd, PyObject * globals, PyObject * locals)
+{
+  bool importLock = PyLockWrapper::ReleaseImportLockIfLocked();
+  ProcessVoidEvent( new TExecutePython(cmd, globals, locals) );
+  if (importLock)
+    PyLockWrapper::AcquireImportLock();
+}
 
 void SalomePyQt::processEvents()
 {
index ba63049a243f2671dd1a50129dd91d59b58d379c..0c82d7cba9922e562161c5d8b8e4493a83f7b419 100644 (file)
@@ -295,8 +295,8 @@ public:
   static QList<double>     getPlot2dFitRangeCurrent(const int);
   static void              setPlot2dFitRange(const int, const double XMin, const double XMax, const double YMin, const double YMax);
 
-//  static void              initializeParaViewGUI();
   static void              processEvents();
+  static void              executePythonInMainThread(const QString& cmd, PyObject * globals, PyObject * locals);
 
   // the following methods are obsolete
   static void              addStringSetting( const QString&, const QString&, bool = true );
index 6344657816e1e62c498d642b6204e8e49eaaaf68..47170384db330a0d348b517e8b4a00ad05763aa7 100644 (file)
@@ -38,6 +38,7 @@
 #include <Plot2d_Curve.h>
 %End
 
+
 class SALOME_Selection : QObject
 {
 %TypeHeaderCode
@@ -466,8 +467,8 @@ public:
   static void              setPlot2dFitRange(const int, const double XMin, const double XMax, const double YMin, const double YMax ) /ReleaseGIL/ ;
 
   static void              processEvents();
+  static void              executePythonInMainThread(const QString& cmd, PyObject * globals, PyObject * locals);
   
-
   static void              startPyLog(const QString&) /ReleaseGIL/ ;
   static void              stopPyLog() /ReleaseGIL/ ;
 };
index 1262d614ed1bd6a1d718c864ac901cb1abf75310..137705ea297a9506e6ab19919f5d122ab69b7138 100755 (executable)
@@ -30,6 +30,7 @@ INCLUDE_DIRECTORIES(
   ${PROJECT_SOURCE_DIR}/src/Qtx
   ${PROJECT_SOURCE_DIR}/src/SUIT
   ${PROJECT_SOURCE_DIR}/src/Style
+  ${PROJECT_SOURCE_DIR}/src/PyInterp
 )
 
 # additional preprocessor / compiler flags
@@ -39,7 +40,7 @@ IF(ENABLE_TESTRECORDER)
 ENDIF()
 
 # libraries to link to
-SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES} qtx suit SalomeStyle)
+SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES} qtx suit SalomeStyle PyInterp)
 IF(SALOME_USE_PYCONSOLE)
   LIST(APPEND _link_LIBRARIES ${PYTHON_LIBRARIES})
 ENDIF()
index 6bf01ab0ee6f0dd1378d06cbb79b7e35ff53bdea..19af19b0d40a55f86b9336cbcafee0c4dbfbc0f2 100644 (file)
@@ -21,6 +21,7 @@
 //  Date   : 22/06/2007
 //
 #include "SUITApp_init_python.hxx"
+#include "PyInterp_Utils.h"
 
 bool SUIT_PYTHON::initialized                       = false;
 
@@ -28,6 +29,7 @@ void SUIT_PYTHON::init_python(int argc, char **argv)
 {
   if (Py_IsInitialized())
   {
+    PyLockWrapper::Initialize();
     return;
   }
   Py_SetProgramName(argv[0]);
@@ -35,6 +37,7 @@ void SUIT_PYTHON::init_python(int argc, char **argv)
   PySys_SetArgv(argc, argv);
   PyEval_InitThreads(); // Create (and acquire) the interpreter lock - can be called many times
 
+  PyLockWrapper::Initialize();
   // Py_InitThreads acquires the GIL
   PyThreadState *pts = PyGILState_GetThisThreadState(); 
   PyEval_ReleaseThread(pts);
index ad228cb3ec5f8d352ff4ad1da8365405b1ac567b..7bb709e68fca799ecd84e9b6a223035f4b79f9db 100755 (executable)
@@ -48,16 +48,6 @@ SalomeApp_PyInterp::~SalomeApp_PyInterp()
 {
 }
  
-/*!
-  Do nothing (we could rely on the test done in the implementation of this method in the super
-  class PyInterp_Interp, but in this context we are sure the initialization has been done in main()
-  of SALOME_Session_Server)
- */
-void SalomeApp_PyInterp::initPython()
-{
-  MESSAGE("SalomeApp_PyInterp::initPython - does nothing");
-}
-
 /*!
   Called before each Python command running.
 */
index bf7bd8d500083006dbfdc8dc30d882969143f511..4fe41274132d85b4701c625ef8f2b2c6dd46a9db 100755 (executable)
@@ -35,7 +35,6 @@ public:
   SalomeApp_PyInterp();
   virtual ~SalomeApp_PyInterp();
 
-  virtual void initPython();
   virtual void initStudy();
   virtual void closeContext();