From 4b0c1cd82a8380c1adaacdfc7691bd7916f1d6d5 Mon Sep 17 00:00:00 2001 From: stv Date: Tue, 2 Oct 2007 10:51:49 +0000 Subject: [PATCH] no message --- src/PyConsole/PyConsole_Editor.cxx | 102 ++++-- src/PyInterp/PyInterp_base.cxx | 549 +++++++++++++++++++++++++++-- src/PyInterp/PyInterp_base.h | 171 ++++++++- 3 files changed, 754 insertions(+), 68 deletions(-) diff --git a/src/PyConsole/PyConsole_Editor.cxx b/src/PyConsole/PyConsole_Editor.cxx index 23268097e..2fee0d516 100644 --- a/src/PyConsole/PyConsole_Editor.cxx +++ b/src/PyConsole/PyConsole_Editor.cxx @@ -108,6 +108,8 @@ #include #include +#include + using namespace std; static QString READY_PROMPT = ">>> "; @@ -117,7 +119,7 @@ static QString DOTS_PROMPT = "... "; /*! \class ExecCommand \brief Python command execution request [internal]. - */ +*/ class ExecCommand : public PyInterp_LockRequest { public: @@ -145,23 +147,16 @@ protected: */ virtual void execute() { - if ( myCommand != "" ) + if( !myCommand.trimmed().isEmpty() ) { // SUIT_Session::SetPythonExecuted( true ); // disable GUI user actions int ret = getInterp()->run( myCommand.toLatin1() ); // SUIT_Session::SetPythonExecuted(false); // enable GUI user actions if ( ret < 0 ) - myState = PyInterp_Event::ERROR; + myState = PyInterp_Event::ERRORS; else if ( ret > 0 ) - myState = PyInterp_Event::INCOMPLETE; - myError = getInterp()->getverr().c_str(); - myOutput = getInterp()->getvout().c_str(); + myState = PyInterp_Event::INCOMPLETE; } - else - { - myError = ""; - myOutput = ""; - } } /*! @@ -173,15 +168,27 @@ protected: return new PyInterp_Event( myState, (PyInterp_Request*)this ); } -public: - QString myError; //!< Python command error message - QString myOutput; //!< Python command output log - private: QString myCommand; //!< Python command int myState; //!< Python command execution status }; +#define PRINT_EVENT 65432 + +class PrintEvent : public QEvent +{ +public: + PrintEvent( const char* c ) : QEvent( (QEvent::Type)PRINT_EVENT ), myText( c ) {} + QString text() const { return myText; } +private: + QString myText; +}; + +void staticCallback( void* data, char* c ) +{ + QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c ) ); +} + /*! \brief Constructor. @@ -203,10 +210,13 @@ PyConsole_Editor::PyConsole_Editor( PyInterp_base* theInterp, setUndoRedoEnabled( false ); myPrompt = READY_PROMPT; - setLineWrapMode( QTextEdit::NoWrap ); - setWordWrapMode( QTextOption::NoWrap ); + setLineWrapMode( QTextEdit::WidgetWidth ); + setWordWrapMode( QTextOption::WrapAnywhere ); setAcceptRichText( false ); + theInterp->setvoutcb( staticCallback, this ); + theInterp->setverrcb( staticCallback, this ); + // san - This is necessary for troubleless initialization onPyInterpChanged( theInterp ); } @@ -236,7 +246,7 @@ void PyConsole_Editor::setIsSync( const bool s ) \param newBlock if True, then the string is printed on a new line */ void PyConsole_Editor::addText( const QString& str, - const bool newBlock ) + const bool newBlock ) { moveCursor( QTextCursor::End ); if ( newBlock ) @@ -297,7 +307,7 @@ PyInterp_Request* PyConsole_Editor::createRequest( const QString& cmd ) \brief Execute command in the python interpreter and wait until it is finished. \param command python command to be executed - */ +*/ void PyConsole_Editor::execAndWait( const QString& command ) { // already running ? @@ -334,6 +344,8 @@ void PyConsole_Editor::handleReturn() if ( !cmd.trimmed().isEmpty() ) myHistory.push_back( cmd ); + addText( "", true ); + // set read-only mode setReadOnly( true ); // set busy cursor @@ -851,28 +863,40 @@ void PyConsole_Editor::customEvent( QEvent* event ) { switch( event->type() ) { - case PyInterp_Event::OK: - case PyInterp_Event::ERROR: - { - PyInterp_Event* pe = dynamic_cast( event ); - if ( pe ) + case PRINT_EVENT: { - ExecCommand* ec = dynamic_cast( pe->GetRequest() ); - if ( ec ) - { - // The next line has appeared dangerous in case if - // Python command execution has produced very large output. - // A more clever approach is needed... - // print python output - addText( ec->myOutput, true ); - addText( ec->myError ); + PrintEvent* pe=(PrintEvent*)event; + + // [ BEGIN ] workaround for the synchronous mode (1.) + if ( isSync() ) { + QTextCursor cur = textCursor(); + cur.movePosition( QTextCursor::End ); + cur.movePosition( QTextCursor::Left, QTextCursor::KeepAnchor, PROMPT_SIZE ); + cur.removeSelectedText(); + setTextCursor( cur ); + } + // [ END ] workaround for the synchronous mode (1.) + + addText( pe->text() ); + + // [ BEGIN ] workaround for the synchronous mode (2.) + if ( isSync() ) { + addText( myPrompt ); } + // [ END ] workaround for the synchronous mode (2.) + + return; } + case PyInterp_Event::OK: + case PyInterp_Event::ERRORS: + { // clear command buffer myCommandBuffer.truncate( 0 ); // set "ready" prompt myPrompt = READY_PROMPT; - addText( myPrompt ); + QTextBlock par = document()->end().previous(); + QString txt = par.text(); + addText( myPrompt, !txt.isEmpty() ); // unset busy cursor unsetCursor(); // stop event loop (if running) @@ -886,7 +910,9 @@ void PyConsole_Editor::customEvent( QEvent* event ) myCommandBuffer.append( "\n" ); // set "dot" prompt myPrompt = DOTS_PROMPT; - addText( myPrompt, true ); + QTextBlock par = document()->end().previous(); + QString txt = par.text(); + addText( myPrompt, !txt.isEmpty() ); // unset busy cursor unsetCursor(); // stop event loop (if running) @@ -958,7 +984,7 @@ void PyConsole_Editor::onPyInterpChanged( PyInterp_base* interp ) Reimplemented from Qt. Warning! In Qt4 this method is not virtual. - */ +*/ void PyConsole_Editor::cut() { QTextCursor cur = textCursor(); @@ -983,7 +1009,7 @@ void PyConsole_Editor::cut() Reimplemented from Qt. Warning! In Qt4 this method is not virtual. - */ +*/ void PyConsole_Editor::paste() { QTextCursor cur = textCursor(); @@ -1010,7 +1036,7 @@ void PyConsole_Editor::paste() Reimplemented from Qt. Warning! In Qt4 this method is not virtual. - */ +*/ void PyConsole_Editor::clear() { QTextEdit::clear(); diff --git a/src/PyInterp/PyInterp_base.cxx b/src/PyInterp/PyInterp_base.cxx index 1ff7cd241..2bd254b62 100644 --- a/src/PyInterp/PyInterp_base.cxx +++ b/src/PyInterp/PyInterp_base.cxx @@ -1,317 +1,810 @@ // Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D + // + // 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. + // + // 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 + // + // SALOME SALOMEGUI : implementation of desktop and GUI kernel + // + // File : PyInterp_base.cxx + // Author : Christian CAREMOLI, Paul RASCLE, EDF + // Module : SALOME + // $Header$ + + + #include "PyInterp_base.h" // this include must be first (see PyInterp_base.h)! + + #include +#include + + + #include + #include + + using namespace std; + + PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState): + myThreadState(theThreadState), + mySaveThreadState(0) + { + #if defined(USE_GILSTATE) + if (myThreadState->interp == PyInterp_base::_interp) { + _savestate = PyGILState_Ensure(); + } else { + PyEval_AcquireThread(myThreadState); + } + #else + PyEval_AcquireThread(myThreadState); + #endif + } + + PyLockWrapper::~PyLockWrapper() + { + #if defined(USE_GILSTATE) + if (myThreadState->interp == PyInterp_base::_interp) { + PyGILState_Release(_savestate); + } else { + PyEval_ReleaseThread(myThreadState); + } + #else + PyEval_ReleaseThread(myThreadState); + #endif + } + + class PyReleaseLock{ + public: + ~PyReleaseLock(){ + PyEval_ReleaseLock(); + } + }; + + + PyLockWrapper PyInterp_base::GetLockWrapper(){ + return _tstate; + +} + + + +static void + +PyStdOut_dealloc(PyStdOut *self) + +{ + + PyObject_Del(self); + +} + + + +static PyObject * + +PyStdOut_write(PyStdOut *self, PyObject *args) + +{ + + char *c; + + 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 { + + self->_cb(self->_data,c); + + } + + Py_INCREF(Py_None); + + return Py_None; + } + +static PyMethodDef PyStdOut_methods[] = { + + {"write", (PyCFunction)PyStdOut_write, METH_VARARGS, + + PyDoc_STR("write(string) -> None")}, + + {NULL, NULL} /* sentinel */ + +}; + + + +static PyMemberDef PyStdOut_memberlist[] = { + + {"softspace", T_INT, offsetof(PyStdOut, softspace), 0, + + "flag indicating that a space needs to be printed; used by print"}, + + {NULL} /* Sentinel */ + +}; + + + + + + + +static PyTypeObject PyStdOut_Type = { + + /* The ob_type field must be initialized in the module init function + + * to be portable to Windows without using C++. */ + + PyObject_HEAD_INIT(NULL) + + 0, /*ob_size*/ + + "PyOut", /*tp_name*/ + + sizeof(PyStdOut), /*tp_basicsize*/ + + 0, /*tp_itemsize*/ + + /* methods */ + + (destructor)PyStdOut_dealloc, /*tp_dealloc*/ + + 0, /*tp_print*/ + + 0, /*tp_getattr*/ + + 0, /*tp_setattr*/ + + 0, /*tp_compare*/ + + 0, /*tp_repr*/ + + 0, /*tp_as_number*/ + + 0, /*tp_as_sequence*/ + + 0, /*tp_as_mapping*/ + + 0, /*tp_hash*/ + + 0, /*tp_call*/ + + 0, /*tp_str*/ + + PyObject_GenericGetAttr, /*tp_getattro*/ + + /* softspace is writable: we must supply tp_setattro */ + + PyObject_GenericSetAttr, /* tp_setattro */ + + 0, /*tp_as_buffer*/ + + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + + 0, /*tp_doc*/ + + 0, /*tp_traverse*/ + + 0, /*tp_clear*/ + + 0, /*tp_richcompare*/ + + 0, /*tp_weaklistoffset*/ + + 0, /*tp_iter*/ + + 0, /*tp_iternext*/ + + PyStdOut_methods, /*tp_methods*/ + + PyStdOut_memberlist, /*tp_members*/ + + 0, /*tp_getset*/ + + 0, /*tp_base*/ + + 0, /*tp_dict*/ + + 0, /*tp_descr_get*/ + + 0, /*tp_descr_set*/ + + 0, /*tp_dictoffset*/ + + 0, /*tp_init*/ + + 0, /*tp_alloc*/ + + 0, /*tp_new*/ + + 0, /*tp_free*/ + + 0, /*tp_is_gc*/ + +}; + + + + + +#define PyStdOut_Check(v) ((v)->ob_type == &PyStdOut_Type) + + + +static PyStdOut * newPyStdOut( bool iscerr ) + +{ + + PyStdOut *self; + + self = PyObject_New(PyStdOut, &PyStdOut_Type); + + if (self == NULL) + + return NULL; + + self->softspace = 0; + + self->_cb = NULL; + + self->_iscerr = iscerr; + + return self; + +} + + + // main python interpreter (static attributes) + + int PyInterp_base::_argc = 1; + char* PyInterp_base::_argv[] = {""}; + + PyObject *PyInterp_base::builtinmodule = NULL; + + PyThreadState *PyInterp_base::_gtstate = NULL; + PyInterpreterState *PyInterp_base::_interp = NULL; + + + /*! + * basic constructor here : herited classes constructors must call initalize() method + * defined here. + */ + PyInterp_base::PyInterp_base(): _tstate(0), _vout(0), _verr(0), _g(0), _atFirst(true) + { + } + + PyInterp_base::~PyInterp_base() + { + } + + + /*! + * Must be called by herited classes constructors. initialize() calls virtuals methods + * initstate & initcontext, not defined here in base class. initstate & initcontext methods + * must be implemented in herited classes, following the Python interpreter policy + * (mono or multi interpreter...). + */ + void PyInterp_base::initialize() + { + _history.clear(); // start a new list of user's commands + _ith = _history.begin(); + + init_python(); + // Here the global lock is released + + initState(); + + PyLockWrapper aLock= GetLockWrapper(); + + initContext(); + + // used to interpret & compile commands + PyObjWrapper m(PyImport_ImportModule("codeop")); + if(!m){ + PyErr_Print(); + return; - } - // Create cStringIO to capture stdout and stderr - PycString_IMPORT; - if (PycStringIO) { // CTH11627 : additional check - _vout = PycStringIO->NewOutput(128); - _verr = PycStringIO->NewOutput(128); } + + + // Create python objects to capture stdout and stderr + + _vout=(PyObject*)newPyStdOut( false ); // stdout + + _verr=(PyObject*)newPyStdOut( true ); // stderr + + + // All the initRun outputs are redirected to the standard output (console) + initRun(); + } + + void PyInterp_base::init_python() + { + _atFirst = false; + if (Py_IsInitialized()) + return; + + // Python is not initialized + Py_SetProgramName(_argv[0]); + Py_Initialize(); // Initialize the interpreter + PySys_SetArgv(_argc, _argv); + PyEval_InitThreads(); // Create (and acquire) the interpreter lock + _interp = PyThreadState_Get()->interp; + + if (PyType_Ready(&PyStdOut_Type) < 0) + + { + + PyErr_Print(); + + } + _gtstate = PyEval_SaveThread(); // Release global thread state + } + + string PyInterp_base::getbanner() + { + // Should we take the lock ? + // PyEval_RestoreThread(_tstate); + string aBanner("Python "); + aBanner = aBanner + Py_GetVersion() + " on " + Py_GetPlatform() ; + aBanner = aBanner + "\ntype help to get general information on environment\n"; + //PyEval_SaveThread(); + return aBanner; + } -int PyInterp_base::initRun() -{ - PySys_SetObject("stderr",_verr); - PySys_SetObject("stdout",_vout); - PyObjWrapper verr(PyObject_CallMethod(_verr,"reset","")); - PyObjWrapper vout(PyObject_CallMethod(_vout,"reset","")); - //PyObject *m = PyImport_GetModuleDict(); - - PySys_SetObject("stdout",PySys_GetObject("__stdout__")); - PySys_SetObject("stderr",PySys_GetObject("__stderr__")); + +int PyInterp_base::initRun() + +{ return 0; + } + + + /*! + * This function compiles a string (command) and then evaluates it in the dictionnary + * context if possible. + * Returns : + * -1 : fatal error + * 1 : incomplete text + * 0 : complete text executed with success + */ + int compile_command(const char *command,PyObject *context) + { + PyObject *m = PyImport_AddModule("codeop"); + if(!m){ // Fatal error. No way to go on. + PyErr_Print(); + return -1; + } + PyObjWrapper v(PyObject_CallMethod(m,"compile_command","s",command)); + if(!v){ + // Error encountered. It should be SyntaxError, + //so we don't write out traceback + PyObjWrapper exception, value, tb; + PyErr_Fetch(&exception, &value, &tb); + PyErr_NormalizeException(&exception, &value, &tb); + PyErr_Display(exception, value, NULL); + return -1; + }else if (v == Py_None){ + // Incomplete text we return 1 : we need a complete text to execute + return 1; + }else{ + // Complete and correct text. We evaluate it. + //#if PY_VERSION_HEX < 0x02040000 // python version earlier than 2.4.0 + // PyObjWrapper r(PyEval_EvalCode(v,context,context)); + //#else + PyObjWrapper r(PyEval_EvalCode((PyCodeObject *)(void *)v,context,context)); + //#endif + if(!r){ + // Execution error. We return -1 + PyErr_Print(); + return -1; + } + // The command has been successfully executed. Return 0 + return 0; + } + } + + + int PyInterp_base::run(const char *command) + { + if(_atFirst){ + int ret = 0; + ret = simpleRun("from Help import *"); + if (ret) { + _atFirst = false; + return ret; + } + ret = simpleRun("import salome"); + if (ret) { + _atFirst = false; + return ret; + } + ret = simpleRun("salome.salome_init(0,1)"); + if (ret) { + _atFirst = false; + return ret; + } + _atFirst = false; + } + return simpleRun(command); + } + + + int PyInterp_base::simpleRun(const char *command) + { + if( !_atFirst && strcmp(command,"") != 0 ) { + _history.push_back(command); + _ith = _history.end(); + } + + // We come from C++ to enter Python world + // We need to acquire the Python global lock + //PyLockWrapper aLock(_tstate); // san - lock is centralized now + + // Reset redirected outputs before treatment + PySys_SetObject("stderr",_verr); + PySys_SetObject("stdout",_vout); - PyObjWrapper verr(PyObject_CallMethod(_verr,"reset","")); - PyObjWrapper vout(PyObject_CallMethod(_vout,"reset","")); + int ier = compile_command(command,_g); + + // Outputs are redirected on standards outputs (console) + PySys_SetObject("stdout",PySys_GetObject("__stdout__")); + PySys_SetObject("stderr",PySys_GetObject("__stderr__")); + + return ier; + } + + + const char * PyInterp_base::getPrevious() + { + if(_ith != _history.begin()){ + _ith--; + return (*_ith).c_str(); + } + else + return BEGIN_HISTORY_PY; + } + + + const char * PyInterp_base::getNext() + { + if(_ith != _history.end()){ + _ith++; + } + if (_ith == _history.end()) + return TOP_HISTORY_PY; + else + return (*_ith).c_str(); + } -string PyInterp_base::getverr(){ - //PyLockWrapper aLock(_tstate); - PyObjWrapper v(PycStringIO->cgetvalue(_verr)); - string aRet(PyString_AsString(v)); - return aRet; + + + +void PyInterp_base::setvoutcb(PyOutChanged* cb, void* data) + +{ + + ((PyStdOut*)_vout)->_cb=cb; + + ((PyStdOut*)_vout)->_data=data; + } -string PyInterp_base::getvout(){ - //PyLockWrapper aLock(_tstate); - PyObjWrapper v(PycStringIO->cgetvalue(_vout)); - string aRet(PyString_AsString(v)); - return aRet; + + + +void PyInterp_base::setverrcb(PyOutChanged* cb, void* data) + +{ + + ((PyStdOut*)_verr)->_cb=cb; + + ((PyStdOut*)_verr)->_data=data; + } + diff --git a/src/PyInterp/PyInterp_base.h b/src/PyInterp/PyInterp_base.h index a00056e86..fd25c0b48 100644 --- a/src/PyInterp/PyInterp_base.h +++ b/src/PyInterp/PyInterp_base.h @@ -1,145 +1,312 @@ // Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D + // + // 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. + // + // 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 + // + // SALOME SALOMEGUI : implementation of desktop and GUI kernel + // + // File : PyInterp_base.h + // Author : Christian CAREMOLI, Paul RASCLE, EDF + // Module : SALOME + + #ifndef _PYINTERP_BASE_H_ + #define _PYINTERP_BASE_H_ + + #include "PyInterp.h" + + #include + #include + #include + + // include order important! + // pthread then python then qt + //#include // must be before Python.h ! + + #include // must be before qt includes ... + #include // Python include needed for versions before 2.4. Included in Python.h now. + #include // Python include needed for versions before 2.4. Included in Python.h now. + + //#if PY_VERSION_HEX < 0x02040000 // python version earlier than 2.4.0 + //extern "C" PyObject * PyEval_EvalCode(PyObject *co, PyObject *g, PyObject *l); + //#endif + + /* For 2.3, use the PyGILState_ calls */ + #if (PY_VERSION_HEX >= 0x02030000) + #define USE_GILSTATE + #endif + + #define TOP_HISTORY_PY "--- top of history ---" + #define BEGIN_HISTORY_PY "--- begin of history ---" + + class PYINTERP_EXPORT PyLockWrapper + { + PyThreadState* myThreadState; + PyThreadState* mySaveThreadState; + #if defined(USE_GILSTATE) + PyGILState_STATE _savestate ; + #endif + public: + PyLockWrapper(PyThreadState* theThreadState); + ~PyLockWrapper(); + }; + + +typedef void PyOutChanged(void* data,char * c); + + + class PYINTERP_EXPORT PyInterp_base{ + public: + static int _argc; + static char* _argv[]; + static PyObject *builtinmodule; + static PyThreadState *_gtstate; + static PyInterpreterState *_interp; + + PyInterp_base(); + virtual ~PyInterp_base(); + + virtual void initialize(); + virtual void init_python(); + // init_python() made virtual to: + // 1. Remove dependency on KERNEL in light SALOME configuration + // 2. Allow redefinition of this method in SalomeApp_PyInterp class (it should be empty there and rely on KERNEL_PYTHON) + + virtual int run(const char *command); + + PyLockWrapper GetLockWrapper(); + + std::string getbanner(); - std::string getverr(); - std::string getvout(); + + void setverrcb(PyOutChanged*,void*); + + void setvoutcb(PyOutChanged*,void*); + + const char * getPrevious(); + const char * getNext(); + + protected: + PyThreadState * _tstate; + PyObject * _vout; + PyObject * _verr; + PyObject * _g; + PyObject * _codeop; + std::list _history; + std::list::iterator _ith; + bool _atFirst; + + int simpleRun(const char* command); + int initRun(); + + virtual bool initState() = 0; + virtual bool initContext() = 0; + }; + + + class PYINTERP_EXPORT PyObjWrapper{ + PyObject* myObject; + public: + PyObjWrapper(PyObject* theObject): myObject(theObject) {} + PyObjWrapper(): myObject(0) {} + 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; + } + virtual ~PyObjWrapper(){ + Py_XDECREF(myObject); + } + }; + + + + +typedef struct { + + PyObject_HEAD + + int softspace; + + PyOutChanged* _cb; + + void* _data; + + bool _iscerr; + +} PyStdOut; + + + #endif + -- 2.39.2