-
Notifications
You must be signed in to change notification settings - Fork 767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PanicException
doesn't inherit from base Exception
#2783
Comments
I tried to explain our stance in #2638 Basically, panics should stay akin to Rust panics, not Python exceptions. It is Finally, if you still disagree it's easy enough to catch |
As a counter argument: One would not expect to get such type of exceptions (e.g. A C++ library wrapped with cython for example would not throw any exception that's not derived from |
First, I'm against strawmanning the current situation as " As for the C++ comparison, in places where Rust code panics, C++ code would typically run into a segfault. |
Agreed with what @birkenfeld has said above. A Rust panic really is intended to be treated as a (graceful) crash, not as a user-space error. It sounds from the polars thread that the maintainers there are willing to treat panics as bugs and fix them.
The point is that a panic isn't a typical error condition (returning Will close this one, if there is significant pressure from the community we can revisit. |
Then again, if a user who is using a If you think it should, I'll then ask, do you think it is reasonable to expect all libraries wrapped with And, if you were using a python library such as |
You repeatedly say this, but it is not correct. While |
Sure, but then it sounds like you do expect users of |
No, not in the majority of cases. As explained above, panics in Rust indicate logic errors and similarly unrecoverable failures that might have corrupted a library's internal state and it usually is the best course of action to e.g. crash the service and let the supervisor layer restart it cleanly. If the Python bindings of polars abuse panics to indicate invalid user input, then that is a bug in those bindings. |
Hi I'd like to ad my 2c here tl;dr: would you accept a PR to add a feature that make On Exception vs BaseExceptionFrom the Python documentation:
From the GeneratorExit documentation
On top of that, So, in Python philosophy, The fact that a panic in Rust should lead to a fatal termination cannot be decided in pyo3 given it depend entirely on what the program does (see example 1 for instance). On Rust panics are special snowflakesThe rational for having Rust panic raising a
However this is not specific to Rust ! Any library in Python can have it internal state corrupted by an unexpected exception. However those exception still inherit And as says the Zen of Python 😄
On how to deal with
|
I don't think is as general as it is put here, or rather this is a fundamental difference in how Rust and Python handle error conditions: Python always uses unwinding whereas Rust differentiates between expected error conditions like I/O errors, unreachable hosts or invalid user input handled via Python has no such distinction besides |
While I've been strongly in the BaseException camp before, I can see that this question is getting raised again and again, since panics-as-termination strongly goes against the Python feeling that "everything can be caught". And since PyO3 is mostly integrating Rust into Python, not the other way round, maybe it really is better to adapt to expectations? Our argument that broken internal state should lead to termination is basically void, since after all it can be caught and will be caught anyway, just with more workarounds and grumbling. |
What matters most for me is which option is most likely to encourage and lead users to write robust and correct programs. Handling panics as exceptions makes it easy to continue running buggy programs (possibly with corrupted internal state) which goes against that. |
As said before: "the users" will do it anyway, just with more grumbling after finding out their expectations were wrong. And under similar circumstances, I did it too, even in pure Rust: in a request handling thread, catch panics and restart that specific thread. Exiting and restarting the whole process was not a good option. |
The bugs in this case are more likely to be on library developers than on users of those libraries. |
Coming back to your first example, how do Python webservers handle things like |
I'd like to reiterate that we are discussing how hard it is to catch Looking at Rust for example, using Similarly, the linked except BaseException as e:
if not "PanicException" in str(type(e)):
raise
# restart threads, reset state, etc. might be ugly, but I think that it would do what is asked and it does not interact with |
Probably not at all - why should they? SystemExit does not simply appear as a result of buggy libraries.
Yes, and since it is pretty clear that our users want to do it, it's just stubbornness to make it surprisingly hard for them.
It is not just ugly, it is also excitingly stringly-typed, and hard to get right. My first instinct was to catch |
As written above when referring to
I am definitely not happy about the stringly type check but I would also like avoid conflating these two issues: That each PyO3 extension has their own type objects and whether The first issue is regularly reported in different contexts as well where it similarly surprises users. We probably need better documentation for this as I hardly see us committing to a stable C ABI/API for sharing type objects any time soon. |
@adamreichold I still fail to understand what is the benefit to make such thing harder 🤔 I've provided two real-life use cases where having Can you provide some examples showing
What about adding a feature to enable
@mejrs the webserver (actually the webframework) don't have to handle |
Cleaning up things as it bubbles up is exactly what a panic does. To me a panic feels very similar to |
As an aside, the "I have a web server that cannot crash no matter what" use case is not a good example. You have to account for your service going down either way. You cannot stop things like aborts/segfaults from bringing down your server. You really should just let the panic do its thing and have a service for restarting your server if it goes down. |
In a segfault, there might be memory corruption extending outside of the library in question, to the point that a program cannot be certain that e.g. In a rust panic, there doesn't need to be any memory corruption that would impede the user to e.g. return an error trace, zero, NaN, or similar; or to do the operation with a non-PyO3 library. |
That is not my point. The server can go down whether or not you are catching
So the usability argument is moot. You still have to be ready for your server crashing. If anything, making it easy to catch
That's not necessarily true; there have been many soundness bugs (including in the standard library) caused by code unwinding out of unsafe code without that unsafe code being ready for it. I have written such buggy unsafe code - it is a really difficult problem. Moreover, safe code might not behave well after catching a panic. The panic might have poisoned a mutex or messed up an invariant somewhere, and any subsequent calls into that library might just panic again. Personally I would not trust that if I caught a panic from an underlying rust library that it would continue to do what I want it to. There is really only one use case for catching panics; catching it, doing something (like logging) and resuming the panic. |
To me a panic is very similar to a Should an unexpected error cause the program to stop ? Maybe.
Python is a memory safe language, Rust is a memory safe language.
That's precisely the point. The application developer is supposed to correctly handle the unexpected exception. If the developer made a mistake in the handling then more exceptions will occur without corrupting memory which is totally what you would expect in a pure-Python code.
You are misinterpreting my words:
See my example 2: it's complex to guarantee the error will be correctly logged and reported when if it inherits
I think you try too hard to go into my shoes here. As a programmer I need to be able to recover from error as long as there is no memory corruption.
That's an interesting point 😃 I would say soundness bugs in 3rd party libraries are out of the scope: any piece of Rust may contain unsafe blocks and do broken dark magic, this wouldn't mean we should consider any pyo3 module broken per-se. I'm really interesting however if you can share an example of the buggy unsafe code you've written 😃 |
I think we don't agree on what a panic is. A panic is not an unexpected error. A panic is a bug in a program. If I write a python library in Rust, and python code can cause a panic in the underlying Rust code that means my library has a bug. Should a bug in a program cause the program to stop? Absolutely. |
This is what a panic looks like in practice in what's perhaps the most popular library using PyO3: import polars as pl
pl.DataFrame({'x':['Some text']}) / 0
|
Polars usage of panics for invalid input and operations is simply incorrect. We consider it a bug in Polars and have said so multiple times in issues and discussions here. |
Surely it is a bug in |
This is a reasonable assumption to make, but I don't think it fits this particular case: This is not an oversight or misunderstanding, but Polars rather made a decision to use panics this way which does not align with the community consensus on when panics or |
Both are not mutually exclusive, a panic is unexpected (as the language give no guarantee it won't happen).
The Erlang people disagree with that (and also basically everybody writing code than involve dead people if the software stops 😄 ) There is no perfect solution here, only strategies with pros and cons. |
Thanks for the discussion but I'm closing this again because I still believe inheriting from Discussion or solutions on making |
From pola-rs/polars#5606
PanicException
doesn't inherit from the base pythonException
, only fromBaseException
.As used by libraries such as
polars
,PanicException
is typically something comparable toValueError
, rather than something comparable toSystemExit
orKeyboardInterrupt
(built-ins which inherit fromBaseException
but not fromException
).Using a library that relies on
pyo3
in some application/service makes error handling problematic, as one most typically adds something likeexcept Exception
in order to handle errors gracefully while still responding to interrupt signals for example, but such code would not catch errors coming from pyo3-wrapped libraries.The text was updated successfully, but these errors were encountered: