[C++-sig] Proposal to improve Python exception handling in Boost.Python

Frank Benkstein benkstein at langer-emv.de
Tue Jun 24 11:35:36 CEST 2008


Hi,

David Abrahams wrote:
> Frank Benkstein wrote:
>> David Abrahams wrote:
> I don't know if you're aware of this, but Boost.Python is currently
> incompatible with the use of Py_Finalize

We currently call Py_Initialize/Py_Finalize in our applications multiple
times while using Boost.Python and didn't notice any problems other than
ensuring that the error indicator is not set, i.e. calling
PyErr_Clear/PyErr_Print.

Is there a document somewhere summarizing what's expected to fail and
what must be done to make it fully work?

[This probably belongs into another thread.]

> All I was saying was that that single function probably ought to include
> the PyErr_Occurred check

If don't understand how that should work.  Do want to call this function
after every call to the C API?  The way I see it every function that
calls the C API has to check for errors itself.  If it detects one
(through a zero or a null pointer return value or a mandatory call to
PyErr_Occurred) it should call a function that knows how to translate
this error to a C++ exception.  Currently this translation is done by
throw_error_already_set.

>> To reiterate:
>>
>>  - If you catch error_already_set the error indicator is set, it is not
>>    safe to call Py_Finalize().
>>  - If you catch exceptions::Exception the error indicator is not set, it
>>    is safe to call Py_Finalize().
> 
> Okay... well, I really dislike the idea of having both things in the
> library at once, and I was never really thrilled with error_already_set.
>  We should think about how to replace it with your thing.  My guess is
> that if the library starts throwing something not derived from
> error_already_set, regardless of whether it leaves the Python error
> there, it's going to break more code than if we changed the meaning of
> error_already_set or "lied" by deriving your thing from it.  There will
> be transition issues, so we should discuss deprecation, compile-time
> checks, runtime checks, etc.

Yes, error_already_set should go.  An intermediate solution in the
deprecation phase could be a runtime switch so new and old code can
coexist.  In the transitional phase the base exception class could
provide a restore method that puts the error indicator back.  This way
code that's handling the Python exceptions itself can be changed from

catch (error_already_set &) {
  ...
}

to

catch (exceptions::python_exception &e) {
  e.restore();
  ...
}

Maybe it's possible to do some wizardry to do that automatically but I
don't know about that.  I also would like to think about deprecation
warnings and compile time switches later when there is any code and it
is in a working shape.

>>>>    And instance members "type", "value" and "traceback" so it can be
>>>>    set or restored as the Python error indicator using PyErr_Restore
>>>>    or PyErr_SetObject if the traceback is None.
>>> Do you really need to store the type in each instance?  Oh, maybe you
>>> do; it could end up being a python exception derived from one of the
>>> standard exceptions.  Then what's "mapped_type" (a.k.a. "python_type")
>>> for?
> 
> Inquiring minds wanna know.

Suppose you want to define a C++ exception class that wraps python's
LookupError.  This could look like the following:

<code>
// Define the wrapper class.
class LookupError {
public:
  // Every wrapper class must have this member.  It is a handle for the
  // Python type object whose instances this class will wrap.
  static object type;

  // This is the real type of the exception.  It may be LookupError but
  // also KeyError or IndexError or something else.
  object real_type;
  // This is the exception instance.
  object value;
  // This is the traceback or None.
  object traceback;

  LookupError(PyObject *t, PyObject *v, PyObject *tb);
};

LookupError::type = handle<>(borrowed(PyExc_LookupError));
</code>

To register the exception you would call something like this somewhere
in your code:

</code>
  exceptions::register<LookupError>();
</code>

After that calls to the function that will replace
throw_error_already_set will throw this LookupError class if
PyErr_ExceptionMatches(LookupError::type.ptr()).

The registry would ensure that subclasses come before their base
classes.

> I think I'd prefer a single analysis and proposal of what should be
> done.  We can talk about how to get there after I understand where we're
> going.

Ok, I think most of the pieces are already there but I'll try to come up
with something more formal.

Best regards,
Frank Benkstein.

-- 
Frank Benkstein

Software Development

Tel.: +49(0) 351 430093-27
Fax : +49(0) 351 430093-22

mailto:benkstein at langer-emv.de

Langer EMV-Technik GmbH
D-01728 Bannewitz
Nöthnitzer Hang 31
Germany

Registergericht: Amtsgericht Dresden, HRB 15402
Geschäftsführer: Gunter Langer

www.langer-emv.de


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 252 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20080624/44f8f95e/attachment.pgp>


More information about the Cplusplus-sig mailing list