[processing] Fix GDAL algorithms hang when gdal command is not available to run

This commit is contained in:
Nyall Dawson 2021-02-20 21:25:51 +10:00
parent 14e5c6094d
commit af0fb8bb87
5 changed files with 60 additions and 3 deletions

View File

@ -129,6 +129,13 @@ After execution completes, the process' result code will be returned.
QProcess::ExitStatus exitStatus() const; QProcess::ExitStatus exitStatus() const;
%Docstring %Docstring
After a call to :py:func:`~QgsBlockingProcess.run`, returns the process' exit status. After a call to :py:func:`~QgsBlockingProcess.run`, returns the process' exit status.
%End
QProcess::ProcessError processError() const;
%Docstring
After a call to :py:func:`~QgsBlockingProcess.run`, returns the process' reported error.
Returns QProcess.UnknownError if no error occurred.
%End %End
}; };

View File

@ -150,6 +150,8 @@ class GdalUtils:
raise QgsProcessingException(GdalUtils.tr('Process was unexpectedly terminated')) raise QgsProcessingException(GdalUtils.tr('Process was unexpectedly terminated'))
elif res == 0: elif res == 0:
feedback.pushInfo(GdalUtils.tr('Process completed successfully')) feedback.pushInfo(GdalUtils.tr('Process completed successfully'))
elif proc.processError() == QProcess.FailedToStart:
raise QgsProcessingException(GdalUtils.tr('Process {} failed to start. Either {} is missing, or you may have insufficient permissions to run the program.').format(command, command))
else: else:
feedback.reportError(GdalUtils.tr('Process returned error code {}').format(res)) feedback.reportError(GdalUtils.tr('Process returned error code {}').format(res))

View File

@ -272,8 +272,9 @@ int QgsBlockingProcess::run( QgsFeedback *feedback )
int result = 0; int result = 0;
QProcess::ExitStatus exitStatus = QProcess::NormalExit; QProcess::ExitStatus exitStatus = QProcess::NormalExit;
QProcess::ProcessError error = QProcess::UnknownError;
std::function<void()> runFunction = [ this, &result, &exitStatus, feedback]() std::function<void()> runFunction = [ this, &result, &exitStatus, &error, feedback]()
{ {
// this function will always be run in worker threads -- either the blocking call is being made in a worker thread, // this function will always be run in worker threads -- either the blocking call is being made in a worker thread,
// or the blocking call has been made from the main thread and we've fired up a new thread for this function // or the blocking call has been made from the main thread and we've fired up a new thread for this function
@ -319,8 +320,16 @@ int QgsBlockingProcess::run( QgsFeedback *feedback )
mStderrHandler( ba ); mStderrHandler( ba );
} ); } );
p.start( mProcess, mArguments, QProcess::Unbuffered | QProcess::ReadWrite ); p.start( mProcess, mArguments, QProcess::Unbuffered | QProcess::ReadWrite );
if ( !p.waitForStarted() )
loop.exec(); {
result = 1;
exitStatus = QProcess::NormalExit;
error = p.error();
}
else
{
loop.exec();
}
mStdoutHandler( p.readAllStandardOutput() ); mStdoutHandler( p.readAllStandardOutput() );
mStderrHandler( p.readAllStandardError() ); mStderrHandler( p.readAllStandardError() );
@ -339,6 +348,7 @@ int QgsBlockingProcess::run( QgsFeedback *feedback )
} }
mExitStatus = exitStatus; mExitStatus = exitStatus;
mProcessError = error;
return result; return result;
} }
@ -346,4 +356,9 @@ QProcess::ExitStatus QgsBlockingProcess::exitStatus() const
{ {
return mExitStatus; return mExitStatus;
}; };
QProcess::ProcessError QgsBlockingProcess::processError() const
{
return mProcessError;
};
#endif // QT_CONFIG(process) #endif // QT_CONFIG(process)

View File

@ -185,6 +185,13 @@ class CORE_EXPORT QgsBlockingProcess : public QObject
*/ */
QProcess::ExitStatus exitStatus() const; QProcess::ExitStatus exitStatus() const;
/**
* After a call to run(), returns the process' reported error.
*
* Returns QProcess::UnknownError if no error occurred.
*/
QProcess::ProcessError processError() const;
private: private:
QString mProcess; QString mProcess;
@ -193,6 +200,7 @@ class CORE_EXPORT QgsBlockingProcess : public QObject
std::function< void( const QByteArray & ) > mStderrHandler; std::function< void( const QByteArray & ) > mStderrHandler;
QProcess::ExitStatus mExitStatus = QProcess::NormalExit; QProcess::ExitStatus mExitStatus = QProcess::NormalExit;
QProcess::ProcessError mProcessError = QProcess::UnknownError;
}; };
#endif // QT_CONFIG(process) #endif // QT_CONFIG(process)

View File

@ -113,6 +113,31 @@ class TestQgsBlockingProcess(unittest.TestCase):
self.assertNotEqual(p.run(f), 0) self.assertNotEqual(p.run(f), 0)
self.assertEqual(p.exitStatus(), QProcess.CrashExit) self.assertEqual(p.exitStatus(), QProcess.CrashExit)
def test_process_no_file(self):
"""
Test a script which doesn't exist
"""
def std_out(ba):
std_out.val += ba.data().decode('UTF-8')
std_out.val = ''
def std_err(ba):
std_err.val += ba.data().decode('UTF-8')
std_err.val = ''
# this program definitely doesn't exist!
p = QgsBlockingProcess('qgis_sucks', ['--version'])
p.setStdOutHandler(std_out)
p.setStdErrHandler(std_err)
f = QgsFeedback()
self.assertEqual(p.run(f), 1)
self.assertEqual(p.exitStatus(), QProcess.NormalExit)
self.assertEqual(p.processError(), QProcess.FailedToStart)
def test_process_env(self): def test_process_env(self):
""" """
Test that process inherits system environment correctly Test that process inherits system environment correctly