From 2207c30a247e5907a12185165b4bd220e255bbdc Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 8 Oct 2020 10:19:28 +1000 Subject: [PATCH] When calling processing.run() and an QgsProcessingExpection occurs, don't raise a generic "something went wrong" exception but instead ensure that the original exception with the proper error message is raised for catching in Python instead --- .../processing/qgsprocessingalgorithm.sip.in | 6 +++++- python/plugins/processing/core/Processing.py | 2 +- .../processing/gui/AlgorithmExecutor.py | 20 +++++++++++-------- .../processing/qgsprocessingalgorithm.cpp | 5 ++++- src/core/processing/qgsprocessingalgorithm.h | 6 +++++- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in b/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in index 7223652e2360..050f12e8009e 100644 --- a/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in +++ b/python/core/auto_generated/processing/qgsprocessingalgorithm.sip.in @@ -337,7 +337,8 @@ is passed in order to make a best guess determination of the output properties. .. versionadded:: 3.14 %End - QVariantMap run( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, bool *ok /Out/ = 0, const QVariantMap &configuration = QVariantMap() ) const; + QVariantMap run( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, bool *ok /Out/ = 0, const QVariantMap &configuration = QVariantMap(), + bool catchExceptions = true ) const throw( QgsProcessingException ); %Docstring Executes the algorithm using the specified ``parameters``. This method internally creates a copy of the algorithm before running it, so it is safe to call @@ -349,6 +350,9 @@ Algorithm progress should be reported using the supplied ``feedback`` object. If specified, ``ok`` will be set to ``True`` if algorithm was successfully run. +If ``catchExceptions`` is set to ``False``, then QgsProcessingExceptions raised during +the algorithm run will not be automatically caught and will be raised instead. + :return: A map of algorithm outputs. These may be output layer references, or calculated values such as statistical calculations. diff --git a/python/plugins/processing/core/Processing.py b/python/plugins/processing/core/Processing.py index ac0cae21ff2d..6b6e6d5a72df 100644 --- a/python/plugins/processing/core/Processing.py +++ b/python/plugins/processing/core/Processing.py @@ -156,7 +156,7 @@ def runAlgorithm(algOrName, parameters, onFinish=None, feedback=None, context=No feedback.pushInfo( Processing.tr('Warning: Not all input layers use the same CRS.\nThis can cause unexpected results.')) - ret, results = execute(alg, parameters, context, feedback) + ret, results = execute(alg, parameters, context, feedback, catch_exceptions=False) if ret: feedback.pushInfo( Processing.tr('Results: {}').format(results)) diff --git a/python/plugins/processing/gui/AlgorithmExecutor.py b/python/plugins/processing/gui/AlgorithmExecutor.py index fcae7185525e..599ef0c000d3 100644 --- a/python/plugins/processing/gui/AlgorithmExecutor.py +++ b/python/plugins/processing/gui/AlgorithmExecutor.py @@ -45,7 +45,7 @@ from qgis.utils import iface -def execute(alg, parameters, context=None, feedback=None): +def execute(alg, parameters, context=None, feedback=None, catch_exceptions=True): """Executes a given algorithm, showing its progress in the progress object passed along. @@ -58,14 +58,18 @@ def execute(alg, parameters, context=None, feedback=None): if context is None: context = dataobjects.createContext(feedback) - try: - results, ok = alg.run(parameters, context, feedback) + if catch_exceptions: + try: + results, ok = alg.run(parameters, context, feedback) + return ok, results + except QgsProcessingException as e: + QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) + if feedback is not None: + feedback.reportError(e.msg) + return False, {} + else: + results, ok = alg.run(parameters, context, feedback, {}, False) return ok, results - except QgsProcessingException as e: - QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical) - if feedback is not None: - feedback.reportError(e.msg) - return False, {} def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False): diff --git a/src/core/processing/qgsprocessingalgorithm.cpp b/src/core/processing/qgsprocessingalgorithm.cpp index 7c8575c3e4d5..e3b56b7acd08 100644 --- a/src/core/processing/qgsprocessingalgorithm.cpp +++ b/src/core/processing/qgsprocessingalgorithm.cpp @@ -445,7 +445,7 @@ QgsProcessingAlgorithm::VectorProperties QgsProcessingAlgorithm::sinkProperties( return VectorProperties(); } -QVariantMap QgsProcessingAlgorithm::run( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, bool *ok, const QVariantMap &configuration ) const +QVariantMap QgsProcessingAlgorithm::run( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, bool *ok, const QVariantMap &configuration, bool catchExceptions ) const { std::unique_ptr< QgsProcessingAlgorithm > alg( create( configuration ) ); if ( ok ) @@ -462,6 +462,9 @@ QVariantMap QgsProcessingAlgorithm::run( const QVariantMap ¶meters, QgsProce } catch ( QgsProcessingException &e ) { + if ( !catchExceptions ) + throw e; + QgsMessageLog::logMessage( e.what(), QObject::tr( "Processing" ), Qgis::Critical ); feedback->reportError( e.what() ); return QVariantMap(); diff --git a/src/core/processing/qgsprocessingalgorithm.h b/src/core/processing/qgsprocessingalgorithm.h index e30a133e893c..9ef78f76c460 100644 --- a/src/core/processing/qgsprocessingalgorithm.h +++ b/src/core/processing/qgsprocessingalgorithm.h @@ -378,13 +378,17 @@ class CORE_EXPORT QgsProcessingAlgorithm * * If specified, \a ok will be set to TRUE if algorithm was successfully run. * + * If \a catchExceptions is set to FALSE, then QgsProcessingExceptions raised during + * the algorithm run will not be automatically caught and will be raised instead. + * * \returns A map of algorithm outputs. These may be output layer references, or calculated * values such as statistical calculations. * * \note this method can only be called from the main thread. Use prepare(), runPrepared() and postProcess() * if you need to run algorithms from a background thread, or use the QgsProcessingAlgRunnerTask class. */ - QVariantMap run( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, bool *ok SIP_OUT = nullptr, const QVariantMap &configuration = QVariantMap() ) const; + QVariantMap run( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback, bool *ok SIP_OUT = nullptr, const QVariantMap &configuration = QVariantMap(), + bool catchExceptions = true ) const SIP_THROW( QgsProcessingException ); /** * Prepares the algorithm for execution. This must be run in the main thread, and allows the algorithm