[Python-ideas] Arguments to exceptions

Nick Coghlan ncoghlan at gmail.com
Mon Jul 17 02:27:40 EDT 2017


On 17 July 2017 at 04:59, Ken Kundert <python-ideas at shalmirane.com> wrote:
> Nick,
>     I see. The Python C interface provides a very simple way of raising an
> exception in the case where the exception is only passed one argument, the error
> message. It even makes it easy to interpolate arguments into that error message.
> If it is important to include other arguments, as with OSError, one would
> presumably use another mechanism that requires more effort.
>
> Your suggestion of providing an alternative to PyErr_Format() that squirreled
> away its arguments into the exception and deferred their interpolation into the
> error message seems like a very nice approach. But as you say, it does not seem
> trivial to implement in C. If it could be extended to support named arguments,
> it does have the benefit that it could unify the way exceptions are raise from
> C.

I'll note that while it wouldn't be trivial, it *should* at least
theoretically be possible to share the string segmentation and
processing steps with the f-string implementation.

However, I also realised that a much simpler approach to the same idea
would instead look like:

    PyErr_SetFromAttributes(PyExc_NameError, "name '%.200s' is not
defined", "name", name);

Where the field names alternate with the field values in the argument
list, rather than being embedded in the format string.

Ignoring error handling, the implementation would then be something like:

    - split va_list into a list of field names and a list of field values
    - set the exception via `PyErr_SetObject(exc_type,
PyUnicode_Format(exc_msg, field_values))`
    - retrieve the just set exception and do `PyObject_SetAttr(exc,
field_name, field_value)` for each field with a non-NULL name

> The alternative would be to simply enhance the individual exceptions on an as
> needed basis as you suggested earlier. That could be easy if the exceptions are
> only raised in one or two places. Do you have a sense for how many places raise
> some of these common exceptions such as NameError, KeyError, etc.?

A quick check shows around a dozen hits for PyExc_NameError in c files
(all in ceval.c), and most of the hits for NameError in Python files
being related to catching it rather than raising it.

PyExc_KeyError is mentioned in a dozen or so C files (most of them
raising it, a few of them check for it being raised elsewhere), and
while the hits in Python files still mostly favour catching it, it's
raised explicitly in the standard library more often than NameError
is.

The nice thing about the PyErr_SetFromAttributes approach at the C API
level is that it pairs nicely with the following approach at the
constructor level:

    class NameError(Exception):
        def __init__(self, message, *, name=None):
            super().__init__(message)
            self.name = name

Something like KeyError may want to distinguish between "key value was
None" and "key value wasn't reported when raising the exception" (by
not defining the attribute at all in the latter case), but
PyErr_SetFromAttributes wouldn't need to care about those kinds of
details.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-ideas mailing list