[QGIS-Developer] PSA: The new SIP_THROW macro

Nyall Dawson nyall.dawson at gmail.com
Tue May 15 16:18:31 PDT 2018


Hi all,

Just a quick heads up about a recent addition to QGIS 3.2 regarding
the Python bindings.

In https://github.com/qgis/QGIS/commit/0f78277 I've added a new
"SIP_THROW" macro. The idea behind this is that SIP uses the
deprecated "throw(...)" annotations in order to determine which
exceptions may be thrown by c++ code. Without these, only a generic
unknown exception is throw, which is of limited value to Python code
(losing any valuable message and exception type). (side note - this
took a LONG time for me to discover... I don't believe it's explicitly
stated anywhere in the sip documentation, at least not that I've been
able to find).

So in this commit I've added a new SIP_THROW macro, which can be added
to a method's declaration:

    bool doSomething() SIP_THROW( QgsCsException );

This is ignored outside of sipify, so we don't actually use the
deprecated c++ throw annotations when compiling, but sipify picks it
up and adds the appropriate change to the sip definition for the
method:

   bool doSomething() throw( QgsCsException );

Without the throw annotation the generated sip code looks something like this:

            try
            {
            sipCpp= new  ::(sipCpp->doSomething());
            }
            catch (...)
            {
                ...
                sipRaiseUnknownException();
                return NULL;
            }

So when the doSomething raises a c++ exception, all the Python caller
gets is the unknown generic exception.

Using the throw annotation the generated code gets expanded to something like:

            try
            {
            sipRes = new  ::(sipCpp->doSomething());
            }
            catch (QgsCsException &sipExceptionRef)
            {
                ...
                PyErr_SetString(sipException_QgsCsException,
sipExceptionRef.what().toUtf8().constData() );
               ...
                return NULL;
            }
            catch (...)
            {
                ....
                sipRaiseUnknownException();
                return NULL;
            }

This means that calling the method from Python will raise the
QgsCsException (when thrown by the c++ method) instead of a generic
exception, giving much more useful debugging information to the caller
and allowing them to handle the specific exception type.

I've made this change throughout Processing (for
QgsProcessingException), so now there's much more explicit error
messages given to both Python and end users of processing algorithms.
It'd be nice to start adding the same for QgsCsException to any
methods which can throw this exception... so if you come across one of
these methods in the c++ code which is missing the SIP_THROW
annotation, please feel free to go ahead and add it!

Cheers,
Nyall


More information about the QGIS-Developer mailing list