Robert Bradshaw schrieb am 05.01.2017 um 19:12:
> On Thu, Jan 5, 2017 at 2:31 AM, Stefan Behnel wrote:
>> Robert Bradshaw schrieb am 04.01.2017 um 07:46:
>>> By default a cdef (or cpdef) function returning C type suppresses all
>>> exceptions, which is quite surprising to new (and old) users, and
>>> makes debugging difficult. I would propose that we make exception
>>> propagation the default.
>>> [...]
>>> - Should we add syntax (e.g. "except -") to manually obtain the old
>>> behavior?
>>
>> Question is: when would we want that? Should we really trade the weird
>> WriteUnraisable() behaviour for speed here? I think, an unraisable
>> exception is always a bug, even if it doesn't appear in practice. If
>> there's not enough memory, exceptions can occur in any place, and shadowing
>> them in unexpected places can do arbitrary harm to your system, especially
>> if it doesn't crash but continues running in an inconsistent state.
> 
> There is the case that the function in question is a callback with no
> ways to report errors--one might prefer to at least print something.

Ah, yes. For some reason, I hadn't thought of callbacks when I wrote that.

The current behaviour is for void functions to print the exception and
return, and for non-void functions to print the exception and return the
default value of the return type, usually -1. We would change that to
keeping the exception and returning a different value (if non-void). The
different return value might have an impact on the calling code, although
it could be argued that even returning -1 can already trigger bugs.

There are a couple of patterns for exception handling in callbacks.

1) Print and ignore exception. This is what happens currently, with the
caveat of returning -1 or NULL behind the user's back. There is currently
no way to change that value, but it can be dealt with in the same way as
the example in 3) below, also for void functions.

2) Keep the exception alive, so that some outer caller can handle it. This
can be done with the "except" signature declarations.

3) Store exception away and return. This is actually more tricky than you
might think, as storing away (or printing or logging) the exception might
fail and raise a new exception. There is a code pattern for this, though:

    cdef int callback():
        error = True
        try:
            do_stuff()
            error = False
        except:
            store_raised_exception()  # might raise!
        finally:
            # swallow any further exceptions
            return -1 if error else 0

Explicitly returning from a finally clause eats the exception, whereas
otherwise the except clause might raise its own exceptions, even just while
trying to retrieve the current exception (i.e. before even entering the
body of the except clause). By leaving out the except clause, any exception
will be silently ignored, but I'd obviously not encourage doing that.


Unless I missed something above, I think all use cases can be handled
either via an except signature declaration or via try-except-finally in the
body of the callback function. Since I'd also argue that the current
behaviour is most likely broken if users don't do anything special already,
I'd still vote for doing the switch and letting users fix their (already
buggy) code.

One drawback: currently, we can issue a warning when exceptions cannot be
propagated, which allows users to take action and fix their code more
easily. In the future, we would no longer be able to warn because Cython
cannot know what functions are callbacks and which aren't. That would
actually make it more difficult for users to adapt to the new behaviour.

Stefan

_______________________________________________
cython-devel mailing list
cython-devel@python.org
https://mail.python.org/mailman/listinfo/cython-devel

Reply via email to