Skip to content

Commit 07d7e10

Browse files
author
wonder
committed
Even better python errors handling: now displaying traceback, python version and python path.
git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@8496 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent d45948e commit 07d7e10

File tree

2 files changed

+96
-23
lines changed

2 files changed

+96
-23
lines changed

src/app/qgspythonutils.cpp

Lines changed: 94 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ void QgsPythonUtils::initPython(QgisInterface* interface)
4444
mMainModule = PyImport_AddModule("__main__"); // borrowed reference
4545
mMainDict = PyModule_GetDict(mMainModule); // borrowed reference
4646

47-
// import sys module
48-
runString("import sys");
47+
runString("import sys"); // import sys module (for display / exception hooks)
48+
runString("import traceback"); // for formatting stack traces
49+
runString("import __main__"); // to access explicitly global variables
50+
4951

5052
// expect that bindings are installed locally, so add the path to modules
5153
// also add path to plugins
@@ -75,23 +77,22 @@ void QgsPythonUtils::initPython(QgisInterface* interface)
7577
return;
7678
}
7779

78-
runString("import __main__");
79-
runString("import traceback"); // for formatting stack traces
80-
8180
// hook that will show information and traceback in message box
8281
runString(
8382
"def qgis_except_hook_msg(type, value, tb, msg):\n"
8483
" lst = traceback.format_exception(type, value, tb)\n"
85-
" if msg == None: msg = 'An error has occured while executing Python code:'\n"
86-
" str = '<font color=\"red\">'+msg+'</font><br><br>'\n"
84+
" if msg == None: msg = '" + QObject::tr("An error has occured while executing Python code:") + "'\n"
85+
" txt = '<font color=\"red\">'+msg+'</font><br><br>'\n"
8786
" for s in lst:\n"
88-
" str += s\n"
89-
" str = str.replace('\\n', '<br>')\n"
90-
" str = str.replace(' ', '&nbsp;')\n" // preserve whitespaces for nicer output
87+
" txt += s\n"
88+
" txt += '<br>" + QObject::tr("Python version:") + "<br>' + sys.version + '<br><br>'\n"
89+
" txt += '"+QObject::tr("Python path:") + "' + str(sys.path)\n"
90+
" txt = txt.replace('\\n', '<br>')\n"
91+
" txt = txt.replace(' ', '&nbsp; ')\n" // preserve whitespaces for nicer output
9192
" \n"
9293
" msg = QgsMessageOutput.createMessageOutput()\n"
93-
" msg.setTitle('Error')\n"
94-
" msg.setMessage(str, QgsMessageOutput.MessageHtml)\n"
94+
" msg.setTitle('" + QObject::tr("Python error") + "')\n"
95+
" msg.setMessage(txt, QgsMessageOutput.MessageHtml)\n"
9596
" msg.showMessage()\n");
9697
runString(
9798
"def qgis_except_hook(type, value, tb):\n"
@@ -168,22 +169,21 @@ bool QgsPythonUtils::runString(const QString& command, QString msgOnError)
168169
if (res)
169170
return true;
170171

171-
// an error occured
172-
// fetch error details and show it
173-
QString err_type, err_value;
174-
getError(err_type, err_value);
175-
176172
if (msgOnError.isEmpty())
177173
{
178174
// use some default message if custom hasn't been specified
179175
msgOnError = QObject::tr("An error occured during execution of following code:") + "\n<tt>" + command + "</tt>";
180176
}
181-
msgOnError.replace("\n", "<br>");
182-
QString str = msgOnError + "<br><br>" + QObject::tr("Error details:") + "<br>"
183-
+ QObject::tr("Type:") + " <b>" + err_type + "</b><br>"
184-
+ QObject::tr("Value:") + " <b>" + err_value + "</b>";
185177

186-
// TODO: show sys.path, local variables, traceback
178+
QString traceback = getTraceback();
179+
QString path, version;
180+
evalString("str(sys.path)", path);
181+
evalString("sys.version", version);
182+
183+
QString str = "<font color=\"red\">"+ msgOnError + "</font><br><br>" + traceback + "<br>" +
184+
QObject::tr("Python version:") + "<br>" + version + "<br><br>" +
185+
QObject::tr("Python path:") + "<br>" + path;
186+
str.replace("\n","<br>").replace(" ", "&nbsp; ");
187187

188188
QgsMessageOutput* msg = QgsMessageOutput::createMessageOutput();
189189
msg->setTitle(QObject::tr("Python error"));
@@ -194,6 +194,78 @@ bool QgsPythonUtils::runString(const QString& command, QString msgOnError)
194194
}
195195

196196

197+
QString QgsPythonUtils::getTraceback()
198+
{
199+
#define TRACEBACK_FETCH_ERROR(what) {errMsg = what; goto done;}
200+
201+
202+
QString errMsg;
203+
QString result;
204+
205+
PyObject *modStringIO = NULL;
206+
PyObject *modTB = NULL;
207+
PyObject *obStringIO = NULL;
208+
PyObject *obResult = NULL;
209+
210+
PyObject *type, *value, *traceback;
211+
212+
PyErr_Fetch(&type, &value, &traceback);
213+
PyErr_NormalizeException(&type, &value, &traceback);
214+
215+
modStringIO = PyImport_ImportModule("cStringIO");
216+
if (modStringIO==NULL)
217+
TRACEBACK_FETCH_ERROR("can't import cStringIO");
218+
219+
obStringIO = PyObject_CallMethod(modStringIO, (char*) "StringIO", NULL);
220+
221+
/* Construct a cStringIO object */
222+
if (obStringIO==NULL)
223+
TRACEBACK_FETCH_ERROR("cStringIO.StringIO() failed");
224+
225+
modTB = PyImport_ImportModule("traceback");
226+
if (modTB==NULL)
227+
TRACEBACK_FETCH_ERROR("can't import traceback");
228+
229+
obResult = PyObject_CallMethod(modTB, (char*) "print_exception",
230+
(char*) "OOOOO",
231+
type, value ? value : Py_None,
232+
traceback ? traceback : Py_None,
233+
Py_None,
234+
obStringIO);
235+
236+
if (obResult==NULL)
237+
TRACEBACK_FETCH_ERROR("traceback.print_exception() failed");
238+
Py_DECREF(obResult);
239+
240+
obResult = PyObject_CallMethod(obStringIO, (char*) "getvalue", NULL);
241+
if (obResult==NULL)
242+
TRACEBACK_FETCH_ERROR("getvalue() failed.");
243+
244+
/* And it should be a string all ready to go - duplicate it. */
245+
if (!PyString_Check(obResult))
246+
TRACEBACK_FETCH_ERROR("getvalue() did not return a string");
247+
248+
result = PyString_AsString(obResult);
249+
250+
done:
251+
252+
// All finished - first see if we encountered an error
253+
if (result.isEmpty() && !errMsg.isEmpty())
254+
{
255+
result = errMsg;
256+
}
257+
258+
Py_XDECREF(modStringIO);
259+
Py_XDECREF(modTB);
260+
Py_XDECREF(obStringIO);
261+
Py_XDECREF(obResult);
262+
Py_XDECREF(value);
263+
Py_XDECREF(traceback);
264+
Py_XDECREF(type);
265+
266+
return result;
267+
}
268+
197269
QString QgsPythonUtils::getTypeAsString(PyObject* obj)
198270
{
199271
if (obj == NULL)

src/app/qgspythonutils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ class QgsPythonUtils
114114

115115
static void installErrorHook();
116116

117-
117+
static QString getTraceback();
118+
118119
//! path where
119120
static QString mPluginsPath;
120121

0 commit comments

Comments
 (0)