Skip to content

Commit c47bcc8

Browse files
author
wonder
committedMay 21, 2008
better handling of python errors raised during initialization.
git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@8491 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 1ad0a9d commit c47bcc8

File tree

3 files changed

+61
-47
lines changed

3 files changed

+61
-47
lines changed
 

‎src/app/qgspythondialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ void QgsPythonDialog::on_edtCmdLine_returnPressed()
4343

4444
// when using Py_single_input the return value will be always null
4545
// we're using custom hooks for output and exceptions to show output in console
46-
if (QgsPythonUtils::runString(command))
46+
if (QgsPythonUtils::runStringUnsafe(command))
4747
{
4848
QgsPythonUtils::evalString("sys.stdout.data", output);
4949
QgsPythonUtils::runString("sys.stdout.data = ''");

‎src/app/qgspythonutils.cpp

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "qgsapplication.h"
2424
#include "qgslogger.h"
25+
#include "qgsmessageoutput.h"
2526

2627
#include <QMessageBox>
2728
#include <QStringList>
@@ -51,32 +52,25 @@ void QgsPythonUtils::initPython(QgisInterface* interface)
5152
runString("sys.path = [\"" + homePluginsPath() + "\", \"" + pythonPath() + "\", \"" + pluginsPath() + "\"] + sys.path");
5253

5354
// import SIP
54-
if (!runString("from sip import wrapinstance, unwrapinstance"))
55+
if (!runString("from sip import wrapinstance, unwrapinstance",
56+
QObject::tr("Couldn't load SIP module.") + "\n" + QObject::tr("Python support will be disabled.")))
5557
{
56-
QMessageBox::warning(0, QObject::tr("Python error"),
57-
QObject::tr("Couldn't load SIP module.\nPython support will be disabled."));
58-
PyErr_Clear();
5958
exitPython();
6059
return;
6160
}
6261

6362
// import Qt bindings
64-
if (!runString("from PyQt4 import QtCore, QtGui"))
63+
if (!runString("from PyQt4 import QtCore, QtGui",
64+
QObject::tr("Couldn't load PyQt4.") + "\n" + QObject::tr("Python support will be disabled.")))
6565
{
66-
QMessageBox::warning(0, QObject::tr("Python error"),
67-
QObject::tr("Couldn't load PyQt bindings.\nPython support will be disabled."));
68-
PyErr_Clear();
6966
exitPython();
7067
return;
7168
}
7269

7370
// import QGIS bindings
74-
if (!runString("from qgis.core import *") ||
75-
!runString("from qgis.gui import *"))
71+
QString error_msg = QObject::tr("Couldn't load PyQGIS.") + "\n" + QObject::tr("Python support will be disabled.");
72+
if (!runString("from qgis.core import *", error_msg) || !runString("from qgis.gui import *", error_msg))
7673
{
77-
QMessageBox::warning(0, QObject::tr("Python error"),
78-
QObject::tr("Couldn't load QGIS bindings.\nPython support will be disabled."));
79-
PyErr_Clear();
8074
exitPython();
8175
return;
8276
}
@@ -162,13 +156,43 @@ void QgsPythonUtils::uninstallConsoleHooks()
162156
}
163157

164158

165-
bool QgsPythonUtils::runString(const QString& command)
159+
bool QgsPythonUtils::runStringUnsafe(const QString& command)
166160
{
167161
PyRun_String(command.toLocal8Bit().data(), Py_single_input, mMainDict, mMainDict);
168-
169162
return (PyErr_Occurred() == 0);
170163
}
171164

165+
bool QgsPythonUtils::runString(const QString& command, QString msgOnError)
166+
{
167+
bool res = runStringUnsafe(command);
168+
if (res)
169+
return true;
170+
171+
// an error occured
172+
// fetch error details and show it
173+
QString err_type, err_value;
174+
getError(err_type, err_value);
175+
176+
if (msgOnError.isEmpty())
177+
{
178+
// use some default message if custom hasn't been specified
179+
msgOnError = QObject::tr("An error occured during execution of following code:") + "\n<tt>" + command + "</tt>";
180+
}
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>";
185+
186+
// TODO: show sys.path, local variables, traceback
187+
188+
QgsMessageOutput* msg = QgsMessageOutput::createMessageOutput();
189+
msg->setTitle(QObject::tr("Python error"));
190+
msg->setMessage(str, QgsMessageOutput::MessageHtml);
191+
msg->showMessage();
192+
193+
return res;
194+
}
195+
172196

173197
QString QgsPythonUtils::getTypeAsString(PyObject* obj)
174198
{
@@ -207,7 +231,7 @@ bool QgsPythonUtils::getError(QString& errorClassName, QString& errorText)
207231
PyObject* err_value;
208232
PyObject* err_tb;
209233

210-
// get the exception information
234+
// get the exception information and clear error
211235
PyErr_Fetch(&err_type, &err_value, &err_tb);
212236

213237
// get exception's class name
@@ -223,8 +247,10 @@ bool QgsPythonUtils::getError(QString& errorClassName, QString& errorText)
223247
else
224248
errorText.clear();
225249

226-
// clear exception
227-
PyErr_Clear();
250+
// cleanup
251+
Py_XDECREF(err_type);
252+
Py_XDECREF(err_value);
253+
Py_XDECREF(err_tb);
228254

229255
return true;
230256
}
@@ -373,29 +399,16 @@ bool QgsPythonUtils::startPlugin(QString packageName)
373399
{
374400
QString pluginPythonVar = "plugins['" + packageName + "']";
375401

376-
// create an instance of the plugin
377-
if (!runString(pluginPythonVar + " = " + packageName + ".classFactory(iface)"))
378-
{
379-
PyErr_Print(); // just print to console
380-
PyErr_Clear();
402+
QString errMsg = QObject::tr("Couldn't load plugin ") + packageName;
381403

382-
QMessageBox::warning(0, QObject::tr("Python error"),
383-
QObject::tr("Couldn't load plugin ") + packageName +
384-
QObject::tr(" due an error when calling its classFactory() method"));
404+
// create an instance of the plugin
405+
if (!runString(pluginPythonVar + " = " + packageName + ".classFactory(iface)",
406+
errMsg + QObject::tr(" due an error when calling its classFactory() method")))
385407
return false;
386-
}
387408

388409
// initGui
389-
if (!runString(pluginPythonVar + ".initGui()"))
390-
{
391-
PyErr_Print(); // just print to console
392-
PyErr_Clear();
393-
394-
QMessageBox::warning(0, QObject::tr("Python error"),
395-
QObject::tr("Couldn't load plugin ") + packageName +
396-
QObject::tr(" due an error when calling its initGui() method"));
410+
if (!runString(pluginPythonVar + ".initGui()", errMsg + QObject::tr(" due an error when calling its initGui() method")))
397411
return false;
398-
}
399412

400413
return true;
401414
}
@@ -406,16 +419,12 @@ bool QgsPythonUtils::unloadPlugin(QString packageName)
406419
// unload and delete plugin!
407420
QString varName = "plugins['" + packageName + "']";
408421

409-
if (!runString(varName + ".unload()") ||
410-
!runString("del " + varName))
411-
{
412-
PyErr_Print(); // just print to console
413-
PyErr_Clear();
414-
415-
QMessageBox::warning(0, QObject::tr("Python error"),
416-
QObject::tr("Error while unloading plugin ") + packageName);
422+
QString errMsg = QObject::tr("Error while unloading plugin ") + packageName;
423+
424+
if (!runString(varName + ".unload()", errMsg))
425+
return false;
426+
if (!runString("del " + varName, errMsg))
417427
return false;
418-
}
419428

420429
return true;
421430
}

‎src/app/qgspythonutils.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,13 @@ class QgsPythonUtils
5353

5454
//! run a statement (wrapper for PyRun_String)
5555
//! this command is more advanced as enables error checking etc.
56+
//! when an exception is raised, it shows dialog with exception details
5657
//! @return true if no error occured
57-
static bool runString(const QString& command);
58+
static bool runString(const QString& command, QString msgOnError = QString());
59+
60+
//! run a statement, error reporting is not done
61+
//! @return true if no error occured
62+
static bool runStringUnsafe(const QString& command);
5863

5964
static bool evalString(const QString& command, QString& result);
6065

0 commit comments

Comments
 (0)