[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;
%Docstring
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
};

View File

@ -150,6 +150,8 @@ class GdalUtils:
raise QgsProcessingException(GdalUtils.tr('Process was unexpectedly terminated'))
elif res == 0:
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:
feedback.reportError(GdalUtils.tr('Process returned error code {}').format(res))

View File

@ -272,8 +272,9 @@ int QgsBlockingProcess::run( QgsFeedback *feedback )
int result = 0;
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,
// 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 );
} );
p.start( mProcess, mArguments, QProcess::Unbuffered | QProcess::ReadWrite );
loop.exec();
if ( !p.waitForStarted() )
{
result = 1;
exitStatus = QProcess::NormalExit;
error = p.error();
}
else
{
loop.exec();
}
mStdoutHandler( p.readAllStandardOutput() );
mStderrHandler( p.readAllStandardError() );
@ -339,6 +348,7 @@ int QgsBlockingProcess::run( QgsFeedback *feedback )
}
mExitStatus = exitStatus;
mProcessError = error;
return result;
}
@ -346,4 +356,9 @@ QProcess::ExitStatus QgsBlockingProcess::exitStatus() const
{
return mExitStatus;
};
QProcess::ProcessError QgsBlockingProcess::processError() const
{
return mProcessError;
};
#endif // QT_CONFIG(process)

View File

@ -185,6 +185,13 @@ class CORE_EXPORT QgsBlockingProcess : public QObject
*/
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:
QString mProcess;
@ -193,6 +200,7 @@ class CORE_EXPORT QgsBlockingProcess : public QObject
std::function< void( const QByteArray & ) > mStderrHandler;
QProcess::ExitStatus mExitStatus = QProcess::NormalExit;
QProcess::ProcessError mProcessError = QProcess::UnknownError;
};
#endif // QT_CONFIG(process)

View File

@ -113,6 +113,31 @@ class TestQgsBlockingProcess(unittest.TestCase):
self.assertNotEqual(p.run(f), 0)
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):
"""
Test that process inherits system environment correctly