From 4443fddf81420c56f6516bf0091633d40b35edb5 2013-01-29 14:18:47 From: florianlink Date: 2013-01-29 14:18:47 Subject: [PATCH] integrated changes from https://github.com/commontk/PythonQt#patched-2 renamed errorOccurred() to hadError() renamed resetErrorFlag() to clearError() added optional system exit handler added iatty method git-svn-id: svn://svn.code.sf.net/p/pythonqt/code/trunk@242 ea8d5007-eb21-0410-b261-ccb3ea6e24a9 --- diff --git a/src/PythonQt.cpp b/src/PythonQt.cpp index f4bb729..a5e8c6f 100644 --- a/src/PythonQt.cpp +++ b/src/PythonQt.cpp @@ -687,6 +687,7 @@ PythonQtObjectPtr PythonQt::importModule(const QString& name) QVariant PythonQt::evalCode(PyObject* object, PyObject* pycode) { QVariant result; + clearError(); if (pycode) { PyObject* dict = NULL; PyObject* globals = NULL; @@ -721,6 +722,7 @@ QVariant PythonQt::evalScript(PyObject* object, const QString& script, int start QVariant result; PythonQtObjectPtr p; PyObject* dict = NULL; + clearError(); if (PyModule_Check(object)) { dict = PyModule_GetDict(object); } else if (PyDict_Check(object)) { @@ -740,6 +742,7 @@ QVariant PythonQt::evalScript(PyObject* object, const QString& script, int start void PythonQt::evalFile(PyObject* module, const QString& filename) { PythonQtObjectPtr code = parseFile(filename); + clearError(); if (code) { evalCode(module, code); } else { @@ -751,6 +754,7 @@ PythonQtObjectPtr PythonQt::parseFile(const QString& filename) { PythonQtObjectPtr p; p.setNewRef(PythonQtImport::getCodeFromPyc(filename)); + clearError(); if (!p) { handleError(); } @@ -1040,6 +1044,7 @@ QVariant PythonQt::call(PyObject* callable, const QVariantList& args, const QVar QVariant r; PythonQtObjectPtr result; result.setNewRef(callAndReturnPyObject(callable, args, kwargs)); + clearError(); if (result) { r = PythonQtConv::PyObjToQVariant(result); } else { @@ -1156,6 +1161,8 @@ PythonQtPrivate::PythonQtPrivate() _wrappedCB = NULL; _currentClassInfoForClassWrapperCreation = NULL; _profilingCB = NULL; + _hadError = false; + _systemExitExceptionHandlerEnabled = false; } void PythonQtPrivate::setupSharedLibrarySuffixes() @@ -1252,31 +1259,119 @@ void PythonQtPrivate::removeSignalEmitter(QObject* obj) _signalReceivers.remove(obj); } +namespace +{ +//! adapted from python source file "pythonrun.c", function "handle_system_exit" +//! return the exitcode instead of calling "Py_Exit". +//! it gives the application an opportunity to properly terminate. +int custom_system_exit_exception_handler() +{ + PyObject *exception, *value, *tb; + int exitcode = 0; + +// if (Py_InspectFlag) +// /* Don't exit if -i flag was given. This flag is set to 0 +// * when entering interactive mode for inspecting. */ +// return exitcode; + + PyErr_Fetch(&exception, &value, &tb); + if (Py_FlushLine()) + PyErr_Clear(); + fflush(stdout); + if (value == NULL || value == Py_None) + goto done; + if (PyExceptionInstance_Check(value)) { + /* The error code should be in the `code' attribute. */ + PyObject *code = PyObject_GetAttrString(value, "code"); + if (code) { + Py_DECREF(value); + value = code; + if (value == Py_None) + goto done; + } + /* If we failed to dig out the 'code' attribute, + just let the else clause below print the error. */ + } + if (PyInt_Check(value)) + exitcode = (int)PyInt_AsLong(value); + else { + PyObject *sys_stderr = PySys_GetObject(const_cast("stderr")); + if (sys_stderr != NULL && sys_stderr != Py_None) { + PyFile_WriteObject(value, sys_stderr, Py_PRINT_RAW); + } else { + PyObject_Print(value, stderr, Py_PRINT_RAW); + fflush(stderr); + } + PySys_WriteStderr("\n"); + exitcode = 1; + } + done: + /* Restore and clear the exception info, in order to properly decref + * the exception, value, and traceback. If we just exit instead, + * these leak, which confuses PYTHONDUMPREFS output, and may prevent + * some finalizers from running. + */ + PyErr_Restore(exception, value, tb); + PyErr_Clear(); + return exitcode; + //Py_Exit(exitcode); +} +} + bool PythonQt::handleError() { bool flag = false; if (PyErr_Occurred()) { - // currently we just print the error and the stderr handler parses the errors - PyErr_Print(); - - /* - // EXTRA: the format of the ptype and ptraceback is not really documented, so I use PyErr_Print() above - PyObject *ptype; - PyObject *pvalue; - PyObject *ptraceback; - PyErr_Fetch( &ptype, &pvalue, &ptraceback); - - Py_XDECREF(ptype); - Py_XDECREF(pvalue); - Py_XDECREF(ptraceback); - */ - PyErr_Clear(); + if (_p->_systemExitExceptionHandlerEnabled && + PyErr_ExceptionMatches(PyExc_SystemExit)) { + int exitcode = custom_system_exit_exception_handler(); + emit PythonQt::self()->systemExitExceptionRaised(exitcode); + } + else + { + // currently we just print the error and the stderr handler parses the errors + PyErr_Print(); + + /* + // EXTRA: the format of the ptype and ptraceback is not really documented, so I use PyErr_Print() above + PyObject *ptype; + PyObject *pvalue; + PyObject *ptraceback; + PyErr_Fetch( &ptype, &pvalue, &ptraceback); + + Py_XDECREF(ptype); + Py_XDECREF(pvalue); + Py_XDECREF(ptraceback); + */ + PyErr_Clear(); + } flag = true; } + _p->_hadError = flag; return flag; } +bool PythonQt::hadError()const +{ + return _p->_hadError; +} + +void PythonQt::clearError() +{ + _p->_hadError = false; +} + +void PythonQt::setSystemExitExceptionHandlerEnabled(bool value) +{ + _p->_systemExitExceptionHandlerEnabled = value; +} + +bool PythonQt::systemExitExceptionHandlerEnabled() const +{ + return _p->_systemExitExceptionHandlerEnabled; +} + void PythonQt::addSysPath(const QString& path) { PythonQtObjectPtr sys; @@ -1602,6 +1697,7 @@ PythonQtInstanceWrapper* PythonQtPrivate::findWrapperAndRemoveUnused(void* obj) PythonQtObjectPtr PythonQtPrivate::createModule(const QString& name, PyObject* pycode) { PythonQtObjectPtr result; + PythonQt::self()->clearError(); if (pycode) { result.setNewRef(PyImport_ExecCodeModule((char*)name.toLatin1().data(), pycode)); } else { @@ -1793,4 +1889,4 @@ PyObject* PythonQtPrivate::wrapMemoryAsBuffer( void* data, Py_ssize_t size ) { // P3K port needed later on! return PyBuffer_FromReadWriteMemory(data, size); -} \ No newline at end of file +} diff --git a/src/PythonQt.h b/src/PythonQt.h index 60372c0..599ac21 100644 --- a/src/PythonQt.h +++ b/src/PythonQt.h @@ -473,17 +473,32 @@ public: //! get access to internal data (should not be used on the public API, but is used by some C functions) static PythonQtPrivate* priv() { return _self->_p; } + //! clear all NotFound entries on all class infos, to ensure that + //! newly loaded wrappers can add methods even when the object was wrapped by PythonQt before the wrapper was loaded + void clearNotFoundCachedMembers(); + //! handle a python error, call this when a python function fails. If no error occurred, it returns false. //! The error is currently just output to the python stderr, future version might implement better trace printing bool handleError(); - //! clear all NotFound entries on all class infos, to ensure that - //! newly loaded wrappers can add methods even when the object was wrapped by PythonQt before the wrapper was loaded - void clearNotFoundCachedMembers(); + //! return \a true if \a handleError() has been called and an error occured. + bool hadError()const; + + //! reset error flag. After calling this, hadError() will return false. + //! \sa hadError() + void clearError(); + + //! if set to true, the systemExitExceptionRaised signal will be emitted if exception SystemExit is caught + //! \sa handleError() + void setSystemExitExceptionHandlerEnabled(bool value); - //! set a callback that is called when a QObject with parent == NULL is wrapped by pythonqt + //! return \a true if SystemExit exception is handled by PythonQt + //! \sa setSystemExitExceptionHandlerEnabled() + bool systemExitExceptionHandlerEnabled() const; + + //! set a callback that is called when a QObject with parent == NULL is wrapped by PythonQt void setQObjectWrappedCallback(PythonQtQObjectWrappedCB* cb); - //! set a callback that is called when a QObject with parent == NULL is no longer wrapped by pythonqt + //! set a callback that is called when a QObject with parent == NULL is no longer wrapped by PythonQt void setQObjectNoLongerWrappedCallback(PythonQtQObjectNoLongerWrappedCB* cb); //! call the callback if it is set @@ -510,6 +525,11 @@ signals: //! emitted when help() is called on a PythonQt object and \c ExternalHelp is enabled void pythonHelpRequest(const QByteArray& cppClassName); + //! emitted when both custom SystemExit exception handler is enabled and a SystemExit + //! exception is raised. + //! \sa setSystemExitExceptionHandlerEnabled(bool) + void systemExitExceptionRaised(int exitCode); + private: void initPythonQtModule(bool redirectStdOut, const QByteArray& pythonQtModuleName); @@ -714,6 +734,9 @@ private: int _initFlags; int _PythonQtObjectPtr_metaId; + bool _hadError; + bool _systemExitExceptionHandlerEnabled; + friend class PythonQt; }; diff --git a/src/PythonQtPythonInclude.h b/src/PythonQtPythonInclude.h index 6f3b5d8..e443324 100644 --- a/src/PythonQtPythonInclude.h +++ b/src/PythonQtPythonInclude.h @@ -45,6 +45,7 @@ #undef _DEBUG #if defined(_MSC_VER) && _MSC_VER >= 1400 #define _CRT_NOFORCE_MANIFEST 1 +#define _STL_NOFORCE_MANIFEST 1 #endif #include #define _DEBUG diff --git a/src/PythonQtStdOut.cpp b/src/PythonQtStdOut.cpp index 8bdcff0..4604541 100644 --- a/src/PythonQtStdOut.cpp +++ b/src/PythonQtStdOut.cpp @@ -91,7 +91,11 @@ static PyObject *PythonQtStdOutRedirect_flush(PyObject * /*self*/, PyObject * /* return Py_BuildValue(""); } - +static PyObject *PythonQtStdOutRedirect_isatty(PyObject * /*self*/, PyObject * /*args*/) +{ + Py_INCREF(Py_False); + return Py_False; +} static PyMethodDef PythonQtStdOutRedirect_methods[] = { {"write", (PyCFunction)PythonQtStdOutRedirect_write, METH_VARARGS, @@ -99,6 +103,9 @@ static PyMethodDef PythonQtStdOutRedirect_methods[] = { {"flush", (PyCFunction)PythonQtStdOutRedirect_flush, METH_VARARGS, "flush the output, currently not implemented but needed for logging framework" }, + {"isatty", (PyCFunction)PythonQtStdOutRedirect_isatty, METH_NOARGS, + "return False since this object is not a tty-like device. Needed for logging framework" + }, {NULL, NULL, 0 , NULL} /* sentinel */ };