From 6286afa991adca696975c8c2b4d674f8af91c65a Mon Sep 17 00:00:00 2001 From: abn Date: Fri, 19 Sep 2014 15:31:32 +0200 Subject: [PATCH] Python console, various improvements: * clearly separate global and local contexts for execution (not sure this brings something though ...) * some Py_INCREF were missing * cleaner compilation units --- src/PyConsole/PyConsole_EnhInterp.cxx | 4 +- src/PyConsole/PyConsole_Interp.cxx | 22 +----- src/PyConsole/PyConsole_Interp.h | 5 +- src/PyInterp/CMakeLists.txt | 2 + src/PyInterp/PyInterp_Dispatcher.cxx | 75 ------------------ src/PyInterp/PyInterp_Event.cxx | 6 ++ src/PyInterp/PyInterp_Interp.cxx | 77 ++++++++---------- src/PyInterp/PyInterp_Interp.h | 75 ++++-------------- src/PyInterp/PyInterp_Request.cxx | 70 +++++++++++++++++ src/PyInterp/PyInterp_Request.h | 5 +- src/PyInterp/PyInterp_Utils.cxx | 47 +++++++++++ src/PyInterp/PyInterp_Utils.h | 78 +++++++++++++++++++ .../SALOME_PYQT_PyInterp.cxx | 9 ++- .../SALOME_PYQT_PyInterp.h | 2 +- .../SALOME_PYQT_PyModule.cxx | 2 +- src/SalomeApp/SalomeApp_NoteBook.cxx | 4 +- 16 files changed, 270 insertions(+), 213 deletions(-) create mode 100644 src/PyInterp/PyInterp_Utils.cxx create mode 100644 src/PyInterp/PyInterp_Utils.h diff --git a/src/PyConsole/PyConsole_EnhInterp.cxx b/src/PyConsole/PyConsole_EnhInterp.cxx index 8915f0765..1f5defe07 100644 --- a/src/PyConsole/PyConsole_EnhInterp.cxx +++ b/src/PyConsole/PyConsole_EnhInterp.cxx @@ -73,7 +73,7 @@ int PyConsole_EnhInterp::runDirCommand(const QString & dirArgument, const QStrin if (dirArgument.trimmed() != "") cmd = dirArgument + "."; cmd += _last_matches[0] + ".__doc__"; - PyObject * str = PyRun_String(cmd.toStdString().c_str(), Py_eval_input, _context, _context); + PyObject * str = PyRun_String(cmd.toStdString().c_str(), Py_eval_input, _global_context, _local_context); if (!str || str == Py_None || !PyString_Check(str)) { if (!str) @@ -104,7 +104,7 @@ int PyConsole_EnhInterp::runDirAndExtract(const QString& dirArgument, { QRegExp re("^[A-Z].+_[A-Z]+[a-z]+.+$"); // discard SWIG static method, e.g. MEDCouplingUMesh_Blabla QString command("dir(" + dirArgument + ")"); - PyObject * plst = PyRun_String(command.toStdString().c_str(), Py_eval_input, _context, _context); + PyObject * plst = PyRun_String(command.toStdString().c_str(), Py_eval_input, _global_context, _local_context); if(!plst || plst == Py_None) { if(!plst) PyErr_Clear(); diff --git a/src/PyConsole/PyConsole_Interp.cxx b/src/PyConsole/PyConsole_Interp.cxx index d230e9b12..c200a85c0 100644 --- a/src/PyConsole/PyConsole_Interp.cxx +++ b/src/PyConsole/PyConsole_Interp.cxx @@ -22,7 +22,7 @@ // SALOME SALOMEGUI : implementation of desktop and GUI kernel // File : PyConsole_Interp.cxx -// Author : Nicolas REJNERI +// Author : Nicolas REJNERI, Adrien BRUNETON // Module : SALOME // #include "PyConsole_Interp.h" @@ -38,7 +38,6 @@ The method initialize() calls virtuals methods - initPython() to initialize global Python interpreter - //- initState() to initialize embedded interpreter state (OBSOLETE) - initContext() to initialize interpreter internal context - initRun() to prepare interpreter for running commands */ @@ -61,22 +60,3 @@ PyConsole_Interp::~PyConsole_Interp() { } -/*! - \brief Initialize python interpeter context. - - The GIL is assumed to be held. - It is the caller responsability to acquire the GIL. - It must still be held on initContext() exit. - - \return \c true on success -*/ -bool PyConsole_Interp::initContext() -{ - PyObject *m = PyImport_AddModule("__main__"); // interpreter main module (module context) - if(!m){ - PyErr_Print(); - return false; - } - _context = PyModule_GetDict(m); // get interpreter dictionnary context - return true; -} diff --git a/src/PyConsole/PyConsole_Interp.h b/src/PyConsole/PyConsole_Interp.h index d122db0a6..0b929332e 100644 --- a/src/PyConsole/PyConsole_Interp.h +++ b/src/PyConsole/PyConsole_Interp.h @@ -22,7 +22,7 @@ // SALOME SALOMEGUI : implementation of desktop and GUI kernel // File : PyConsole_Interp.h -// Author : Nicolas REJNERI +// Author : Nicolas REJNERI, Adrien BRUNETON // Module : SALOME // #ifndef PYCONSOLE_INTERP_H @@ -37,9 +37,6 @@ class PYCONSOLE_EXPORT PyConsole_Interp : public PyInterp_Interp public: PyConsole_Interp(); ~PyConsole_Interp(); - -protected: - virtual bool initContext(); }; #endif // PYCONSOLE_INTERP_H diff --git a/src/PyInterp/CMakeLists.txt b/src/PyInterp/CMakeLists.txt index 584eb0f7d..f85d651f2 100755 --- a/src/PyInterp/CMakeLists.txt +++ b/src/PyInterp/CMakeLists.txt @@ -47,6 +47,7 @@ SET(_other_HEADERS PyInterp_Event.h PyInterp_Interp.h PyInterp_Request.h + PyInterp_Utils.h ) # header files / to install @@ -63,6 +64,7 @@ SET(_other_SOURCES PyInterp_Event.cxx PyInterp_Interp.cxx PyInterp_Request.cxx + PyInterp_Utils.cxx ) # sources / to compile diff --git a/src/PyInterp/PyInterp_Dispatcher.cxx b/src/PyInterp/PyInterp_Dispatcher.cxx index 73898b53d..40b0059e8 100755 --- a/src/PyInterp/PyInterp_Dispatcher.cxx +++ b/src/PyInterp/PyInterp_Dispatcher.cxx @@ -34,81 +34,6 @@ PyInterp_Dispatcher* PyInterp_Dispatcher::myInstance = 0; -void PyInterp_Request::process() -{ - safeExecute(); - - bool isSync = IsSync(); - - if ( !isSync ) - myMutex.lock(); - - if ( listener() ) - processEvent( listener() ); - - if ( !isSync ) - myMutex.unlock(); -} - -void PyInterp_Request::safeExecute() -{ - //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) ); - execute(); -} - -void PyInterp_Request::Destroy( PyInterp_Request* request ) -{ - // Lock and unlock the mutex to avoid errors on its deletion - request->myMutex.lock(); - request->myMutex.unlock(); - delete request; -} - -QEvent* PyInterp_Request::createEvent() -{ - return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, this ); -} - -void PyInterp_Request::processEvent( QObject* o ) -{ - if ( !o ) - return; - - QEvent* e = createEvent(); - if ( !e ) - return; - - if ( !IsSync() ) - QCoreApplication::postEvent( o, e ); - else - { - QCoreApplication::sendEvent( o, e ); - delete e; - } -} - -void PyInterp_Request::setListener( QObject* o ) -{ - myMutex.lock(); - myListener = o; - myMutex.unlock(); -} - -void PyInterp_LockRequest::safeExecute() -{ - //if ( getInterp() ){ // No need to have an interpreter now! we can simply run in a empty context - PyLockWrapper aLock; // Acquire GIL - //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) ); - execute(); - //} -} - -PyInterp_Event::~PyInterp_Event() -{ - PyInterp_Request::Destroy( myRequest ); - myRequest = 0; -} - PyInterp_Dispatcher* PyInterp_Dispatcher::Get() { if ( !myInstance ) diff --git a/src/PyInterp/PyInterp_Event.cxx b/src/PyInterp/PyInterp_Event.cxx index 398b73e16..0bdfddac2 100644 --- a/src/PyInterp/PyInterp_Event.cxx +++ b/src/PyInterp/PyInterp_Event.cxx @@ -26,6 +26,12 @@ #include "PyInterp_Event.h" #include "PyInterp_Request.h" +PyInterp_Event::~PyInterp_Event() +{ + PyInterp_Request::Destroy( myRequest ); + myRequest = 0; +} + void PyInterp_ExecuteEvent::Execute() { myRequest->execute(); diff --git a/src/PyInterp/PyInterp_Interp.cxx b/src/PyInterp/PyInterp_Interp.cxx index c4ee5d5f6..4664009ef 100644 --- a/src/PyInterp/PyInterp_Interp.cxx +++ b/src/PyInterp/PyInterp_Interp.cxx @@ -21,10 +21,11 @@ // // File : PyInterp_Interp.cxx -// Author : Christian CAREMOLI, Paul RASCLE, EDF +// Author : Christian CAREMOLI, Paul RASCLE, Adrien BRUNETON // Module : SALOME // #include "PyInterp_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!! +#include "PyInterp_Utils.h" #include #include @@ -39,35 +40,6 @@ #define TOP_HISTORY_PY "--- top of history ---" #define BEGIN_HISTORY_PY "--- begin of history ---" -/*! - \class PyLockWrapper - \brief Python GIL wrapper. -*/ - -/*! - \brief Constructor. Automatically acquires GIL. -*/ -PyLockWrapper::PyLockWrapper() -{ - _gil_state = PyGILState_Ensure(); -} - -/*! - \brief Destructor. Automatically releases GIL. -*/ -PyLockWrapper::~PyLockWrapper() -{ -// if (myThreadState->interp == PyInterp_Interp::_interp) -// PyGILState_Release(_savestate); -// else -// PyEval_ReleaseThread(myThreadState); - - /* The destructor can never be called concurrently by two threads since it is called - * when the GIL is held - the below test should never run concurrently in two threads. - */ - PyGILState_Release(_gil_state); -} - /* The following functions are used to hook the Python interpreter output. @@ -187,9 +159,6 @@ static PyStdOut* newPyStdOut( bool iscerr ) int PyInterp_Interp::_argc = 1; char* PyInterp_Interp::_argv[] = {(char*)""}; -//PyObject* PyInterp_Interp::builtinmodule = NULL; -//PyThreadState* PyInterp_Interp::_gtstate = NULL; -//PyInterpreterState* PyInterp_Interp::_interp = NULL; /*! \brief Basic constructor. @@ -198,7 +167,7 @@ char* PyInterp_Interp::_argv[] = {(char*)""}; must call virtual method initalize(). */ PyInterp_Interp::PyInterp_Interp(): - _vout(0), _verr(0), _context(0) + _vout(0), _verr(0), _local_context(0), _global_context(0) { } @@ -209,6 +178,7 @@ PyInterp_Interp::PyInterp_Interp(): */ PyInterp_Interp::~PyInterp_Interp() { + destroy(); } /*! @@ -217,7 +187,6 @@ PyInterp_Interp::~PyInterp_Interp() This method shoud be called after construction of the interpreter. The method initialize() calls virtuals methods - initPython() to initialize global Python interpreter - - initState() to initialize embedded interpreter state - initContext() to initialize interpreter internal context - initRun() to prepare interpreter for running commands which should be implemented in the successor classes, according to the @@ -307,27 +276,49 @@ bool PyInterp_Interp::initRun() return true; } +/*! + * Initialize context dictionaries. GIL is held already. + */ +bool PyInterp_Interp::initContext() +{ + PyObject *m = PyImport_AddModule("__main__"); // interpreter main module (module context) + if(!m){ + PyErr_Print(); + return false; + } + _global_context = PyModule_GetDict(m); // get interpreter global variable context + Py_INCREF(_global_context); + _local_context = PyDict_New(); + return true; +} + +/*! + * Destroy context dictionaries. GIL is held already. + */ void PyInterp_Interp::closeContext() { + Py_XDECREF(_global_context); + Py_XDECREF(_local_context); } /*! \brief Compile Python command and evaluate it in the - python dictionary context if possible. This is not thread-safe. + python dictionary contexts if possible. This is not thread-safe. This is the caller's responsability to make this thread-safe. \internal \param command Python command string - \param context Python context (dictionary) \return -1 on fatal error, 1 if command is incomplete and 0 if command is executed successfully */ -static int run_command(const char *command, PyObject *context) +static int run_command(const char *command, PyObject * global_ctxt, PyObject * local_ctxt) { PyObject *m = PyImport_AddModule("codeop"); if(!m) { // Fatal error. No way to go on. PyErr_Print(); return -1; } + +// PyObjWrapper v(Py_CompileString(command, "", Py_file_input)); PyObjWrapper v(PyObject_CallMethod(m,(char*)"compile_command",(char*)"s",command)); if(!v) { // Error encountered. It should be SyntaxError, @@ -343,7 +334,7 @@ static int run_command(const char *command, PyObject *context) return 1; } else { - PyObjWrapper r(PyEval_EvalCode((PyCodeObject *)(void *)v,context,context)); + PyObjWrapper r(PyEval_EvalCode((PyCodeObject *)(void *)v,global_ctxt, local_ctxt)); if(!r) { // Execution error. We return -1 PyErr_Print(); @@ -375,7 +366,7 @@ void replaceAll(std::string& str, const std::string& from, const std::string& to \return -1 on fatal error, 1 if command is incomplete and 0 if command is executed successfully */ -static int compile_command(const char *command, PyObject *context) +static int compile_command(const char *command, PyObject * global_ctxt, PyObject * local_ctxt) { // First guess if command is execution of a script with args, or a simple Python command std::string singleCommand = command; @@ -391,7 +382,7 @@ static int compile_command(const char *command, PyObject *context) if (commandArgs.empty()) { // process command: expression // process command: execfile(r"/absolute/path/to/script.py") (no args) - return run_command(singleCommand.c_str(), context); + return run_command(singleCommand.c_str(), global_ctxt, local_ctxt); } else { // process command: execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]") @@ -403,7 +394,7 @@ static int compile_command(const char *command, PyObject *context) replaceAll(commandArgs, ",", "\",\""); commandArgs = "\""+commandArgs+"\""; std::string completeCommand = preCommandBegin+"\""+script+"\","+commandArgs+preCommandEnd+singleCommand+";sys.argv=save_argv"; - return run_command(completeCommand.c_str(), context); + return run_command(completeCommand.c_str(), global_ctxt, local_ctxt); } } @@ -452,7 +443,7 @@ 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,_context); + int ier = compile_command(command, _global_context, _local_context); // Outputs are redirected to what they were before PySys_SetObject((char*)"stdout",oldOut); diff --git a/src/PyInterp/PyInterp_Interp.h b/src/PyInterp/PyInterp_Interp.h index 766f02a5e..9b855a4f2 100644 --- a/src/PyInterp/PyInterp_Interp.h +++ b/src/PyInterp/PyInterp_Interp.h @@ -21,39 +21,28 @@ // // File : PyInterp_Interp.h -// Author : Christian CAREMOLI, Paul RASCLE, EDF +// Author : Christian CAREMOLI, Paul RASCLE, Adrien BRUNETON // Module : SALOME // #ifndef PYINTERP_INTERP_H #define PYINTERP_INTERP_H #include "PyInterp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!! +#include "PyInterp_Utils.h" #include #include -/** - * Utility class wrappin 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); -}; - typedef void PyOutChanged(void* data,char * c); +typedef struct { + PyObject_HEAD + int softspace; + PyOutChanged* _cb; + void* _data; + bool _iscerr; +} PyStdOut; + /** * Main class representing a *virtual* Python interpreter. There is really only one true * Python interpreter in the whole application (no call to Py_NewInterpreter), @@ -92,8 +81,9 @@ protected: PyObject * _vout; PyObject * _verr; /** Execution context (local and global variables) */ - PyObject * _context; - PyObject * _codeop; + PyObject * _global_context; + PyObject * _local_context; + std::list _history; std::list::iterator _ith; @@ -101,46 +91,11 @@ protected: int simpleRun(const char* command, const bool addToHistory = true); virtual void initPython(); - /** OBSOLETE - should'nt be called anymore */ - //virtual bool initState() = 0; - /** Initialize execution context. Must set the member _context, and return True on success. */ - virtual bool initContext() = 0; + /** Initialize execution context. */ + virtual bool initContext(); virtual bool initRun(); virtual void closeContext(); }; -/** - * Utility class to properly handle the reference counting required on Python objects. - */ -class PYINTERP_EXPORT PyObjWrapper -{ - PyObject* myObject; -public: - PyObjWrapper(PyObject* theObject) : myObject(theObject) {} - PyObjWrapper() : myObject(0) {} - virtual ~PyObjWrapper() { Py_XDECREF(myObject); } - - operator PyObject*() { return myObject; } - PyObject* operator->() { return myObject; } - PyObject* get() { return myObject; } - bool operator!() { return !myObject; } - bool operator==(PyObject* theObject) { return myObject == theObject; } - PyObject** operator&() { return &myObject; } - PyObjWrapper& operator=(PyObjWrapper* theObjWrapper) - { - Py_XDECREF(myObject); - myObject = theObjWrapper->myObject; - return *this; - } -}; - -typedef struct { - PyObject_HEAD - int softspace; - PyOutChanged* _cb; - void* _data; - bool _iscerr; -} PyStdOut; - #endif // PYINTERP_INTERP_H diff --git a/src/PyInterp/PyInterp_Request.cxx b/src/PyInterp/PyInterp_Request.cxx index eab188f7a..f541a1a06 100644 --- a/src/PyInterp/PyInterp_Request.cxx +++ b/src/PyInterp/PyInterp_Request.cxx @@ -24,4 +24,74 @@ // Module : GUI #include "PyInterp_Request.h" +#include "PyInterp_Utils.h" +#include +void PyInterp_Request::process() +{ + safeExecute(); + + bool isSync = IsSync(); + + if ( !isSync ) + myMutex.lock(); + + if ( listener() ) + processEvent( listener() ); + + if ( !isSync ) + myMutex.unlock(); +} + +void PyInterp_Request::safeExecute() +{ + //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) ); + execute(); +} + +void PyInterp_Request::Destroy( PyInterp_Request* request ) +{ + // Lock and unlock the mutex to avoid errors on its deletion + request->myMutex.lock(); + request->myMutex.unlock(); + delete request; +} + +QEvent* PyInterp_Request::createEvent() +{ + return new PyInterp_Event( PyInterp_Event::ES_NOTIFY, this ); +} + +void PyInterp_Request::processEvent( QObject* o ) +{ + if ( !o ) + return; + + QEvent* e = createEvent(); + if ( !e ) + return; + + if ( !IsSync() ) + QCoreApplication::postEvent( o, e ); + else + { + QCoreApplication::sendEvent( o, e ); + delete e; + } +} + +void PyInterp_Request::setListener( QObject* o ) +{ + myMutex.lock(); + myListener = o; + myMutex.unlock(); +} + +void PyInterp_LockRequest::safeExecute() +{ + //if ( getInterp() ){ // No need to have an interpreter now! we can simply run in a empty context + PyLockWrapper aLock; // Acquire GIL + //ProcessVoidEvent( new PyInterp_ExecuteEvent( this ) ); + execute(); + //} +} diff --git a/src/PyInterp/PyInterp_Request.h b/src/PyInterp/PyInterp_Request.h index 4ceacd3e0..56046869f 100644 --- a/src/PyInterp/PyInterp_Request.h +++ b/src/PyInterp/PyInterp_Request.h @@ -43,6 +43,7 @@ class PYINTERP_EXPORT PyInterp_Request friend class PyInterp_Dispatcher; friend class PyInterp_ExecuteEvent; +private: PyInterp_Request(); PyInterp_Request( const PyInterp_Request& ); @@ -51,7 +52,7 @@ protected: // protected destructor - to control deletion of requests public: - PyInterp_Request( QObject* listener, bool sync = false ) + PyInterp_Request( QObject* listener, bool sync = true ) : myIsSync( sync ), myListener( listener ) {}; static void Destroy( PyInterp_Request* ); @@ -88,7 +89,7 @@ class PYINTERP_EXPORT PyInterp_LockRequest : public PyInterp_Request { public: - PyInterp_LockRequest( PyInterp_Interp* interp, QObject* listener=0, bool sync=false ) + PyInterp_LockRequest( PyInterp_Interp* interp, QObject* listener=0, bool sync=true ) : PyInterp_Request( listener, sync ), myInterp( interp ) {} diff --git a/src/PyInterp/PyInterp_Utils.cxx b/src/PyInterp/PyInterp_Utils.cxx new file mode 100644 index 000000000..4fa3bc2ae --- /dev/null +++ b/src/PyInterp/PyInterp_Utils.cxx @@ -0,0 +1,47 @@ +// Copyright (C) 2007-2014 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Adrien BRUNETON +// + +#include "PyInterp_Utils.h" + +/*! + \class PyLockWrapper + \brief Python GIL wrapper. +*/ + +/*! + \brief Constructor. Automatically acquires GIL. +*/ +PyLockWrapper::PyLockWrapper() +{ + _gil_state = PyGILState_Ensure(); +} + +/*! + \brief Destructor. Automatically releases GIL. +*/ +PyLockWrapper::~PyLockWrapper() +{ + PyGILState_Release(_gil_state); +} + diff --git a/src/PyInterp/PyInterp_Utils.h b/src/PyInterp/PyInterp_Utils.h new file mode 100644 index 000000000..c9866bab1 --- /dev/null +++ b/src/PyInterp/PyInterp_Utils.h @@ -0,0 +1,78 @@ +// Copyright (C) 2007-2014 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// Author : Adrien BRUNETON +// + +#ifndef PYINTERP_UTILS_H +#define PYINTERP_UTILS_H + +#include "PyInterp.h" +#include + +/** + * 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); +}; + + +/** + * Utility class to properly handle the reference counting required on Python objects. + */ +class PYINTERP_EXPORT PyObjWrapper +{ + PyObject* myObject; +public: + PyObjWrapper(PyObject* theObject) : myObject(theObject) {} + PyObjWrapper() : myObject(0) {} + virtual ~PyObjWrapper() { Py_XDECREF(myObject); } + + operator PyObject*() { return myObject; } + PyObject* operator->() { return myObject; } + PyObject* get() { return myObject; } + bool operator!() { return !myObject; } + bool operator==(PyObject* theObject) { return myObject == theObject; } + PyObject** operator&() { return &myObject; } + PyObjWrapper& operator=(PyObjWrapper* theObjWrapper) + { + Py_XDECREF(myObject); + myObject = theObjWrapper->myObject; + return *this; + } +}; + +#endif + diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyInterp.cxx b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyInterp.cxx index bb72bb2b6..c47379393 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyInterp.cxx +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyInterp.cxx @@ -52,6 +52,10 @@ void SALOME_PYQT_PyInterp::initPython() MESSAGE("SALOME_PYQT_PyInterp::initPython - does nothing"); } +/*! + * Override. Create a distinct context from the SALOME Python console. + * Especially the global context is not connected to __main__ as in PyInterp_Interp + */ bool SALOME_PYQT_PyInterp::initContext() { /* @@ -59,7 +63,8 @@ bool SALOME_PYQT_PyInterp::initContext() * It is the caller responsability to acquire the GIL before calling initContext * It will still be held on initContext exit */ - _context = PyDict_New(); // create interpreter dictionnary context + _local_context = PyDict_New(); + _global_context = PyDict_New(); return true; } @@ -72,7 +77,7 @@ int SALOME_PYQT_PyInterp::run(const char *command) PyErr_Print(); return -1; } - PyObject *r = PyEval_EvalCode((PyCodeObject *)code,_context,_context); + PyObject *r = PyEval_EvalCode((PyCodeObject *)code,_global_context,_local_context); Py_DECREF(code); if(!r){ diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyInterp.h b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyInterp.h index 5857ffe8d..795c0770c 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyInterp.h +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyInterp.h @@ -34,7 +34,7 @@ class SALOME_PYQT_LIGHT_EXPORT SALOME_PYQT_PyInterp : public PyInterp_Interp SALOME_PYQT_PyInterp(); ~SALOME_PYQT_PyInterp(); - int run(const char *command); + virtual int run(const char *command); protected: virtual void initPython(); diff --git a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx index d8c21b005..30bd9616e 100644 --- a/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx +++ b/src/SALOME_PYQT/SALOME_PYQT_GUILight/SALOME_PYQT_PyModule.cxx @@ -1096,7 +1096,7 @@ void PyModuleHelper::studyActivated( SUIT_Study* study ) public: StudyChangedReq( PyModuleHelper* _helper, SUIT_Study* _study ) - : PyInterp_Request( 0, true ), // this request should be processed synchronously (sync == true) + : PyInterp_Request(0, true ), // this request should be processed synchronously (sync == true) myHelper( _helper ), myStudy ( _study ) {} diff --git a/src/SalomeApp/SalomeApp_NoteBook.cxx b/src/SalomeApp/SalomeApp_NoteBook.cxx index 45d8dea5b..b5ecb767a 100644 --- a/src/SalomeApp/SalomeApp_NoteBook.cxx +++ b/src/SalomeApp/SalomeApp_NoteBook.cxx @@ -21,8 +21,8 @@ // Author : Roman NIKOLAEV, Open CASCADE S.A.S. // Module : GUI // -#include // this include must be first (see PyInterp_base.h)! -#include +#include "PyConsole_Interp.h" // this include must be first (see PyInterp_base.h)! +#include "PyConsole_Console.h" #include "SalomeApp_NoteBook.h" #include "SalomeApp_Application.h" -- 2.39.2