myCmdInHistory( -1 ),
myEventLoop( 0 ),
myShowBanner( true ),
- myIsSync( true ),
+ myIsSync( false ),
myIsSuppressOutput( false )
{
QString fntSet( "" );
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;
}
*/
PyInterp_Interp::PyInterp_Interp():
_vout(0), _verr(0), _local_context(0), _global_context(0)
-{
-}
-
-
+{}
/*!
\brief Destructor.
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;
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();
}
/*!
/*!
\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;
}
\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);
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);
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*);
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();
//
#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.
*/
_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.
*/
#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.
}
};
+/**
+ * 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
#include "SUIT_ResourceMgr.h"
#include "SUIT_Session.h"
#include "SUIT_Tools.h"
+#include "PyConsole_Interp.h"
#include "PyConsole_Console.h"
#include <QAction>
};
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;
}
/*!
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()
{
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 );
#include <Plot2d_Curve.h>
%End
+
class SALOME_Selection : QObject
{
%TypeHeaderCode
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/ ;
};
${PROJECT_SOURCE_DIR}/src/Qtx
${PROJECT_SOURCE_DIR}/src/SUIT
${PROJECT_SOURCE_DIR}/src/Style
+ ${PROJECT_SOURCE_DIR}/src/PyInterp
)
# additional preprocessor / compiler flags
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()
// Date : 22/06/2007
//
#include "SUITApp_init_python.hxx"
+#include "PyInterp_Utils.h"
bool SUIT_PYTHON::initialized = false;
{
if (Py_IsInitialized())
{
+ PyLockWrapper::Initialize();
return;
}
Py_SetProgramName(argv[0]);
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);
{
}
-/*!
- 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.
*/
SalomeApp_PyInterp();
virtual ~SalomeApp_PyInterp();
- virtual void initPython();
virtual void initStudy();
virtual void closeContext();