
I occasionally found situations where I want to raise an exception for errors that can only arise because the developer made a mistake, for example: - an abstract method is supposed to be reimplemented and its execution is supposed to leave some internal constraints of an object unchanged, but these are instead violated. - an impossible "else" condition after an if/elif, where the else cannot simply happen unless someone really screwed up the internal state of the object. In general, when these cases happen, I use RuntimeError, but other people may choose otherwise. I've also seen ValueError, plain Exception or a specifically made subclass used in these cases. Whatever the choice is, it generally lacks clarity of communication to whoever receives it. RuntimeError is a rather generic exception according to the documentation, and you can only rely on the message, which relies on writing something appropriate for the situation: ``` exception RuntimeError Raised when an error is detected that doesn’t fall in any of the other categories. The associated value is a string indicating what precisely went wrong. ``` while it would be useful to communicate clearly to whoever is using or modifying the code "listen, it's your mistake, you misunderstood how I work or you ruined my internals, leading me to an impossible state". Also, customers seeing this kind of exception would understand without a doubt that the application is broken because of an internal error. I tried some search on the mailing list but could not find anything at a glance about this topic. Was this already discussed in the past? Thanks -- Kind regards, Stefano Borini

That's quite a good idea, but then I think it should be more explicit in the documentation that the purpose goes beyond the assert statement failure. I've never seen AssertionError raised manually. On Wed, 10 Apr 2019 at 23:15, Jeroen Demeyer <J.Demeyer@ugent.be> wrote:
-- Kind regards, Stefano Borini

On Thu, Apr 11, 2019 at 8:15 AM Jeroen Demeyer <J.Demeyer@ugent.be> wrote:
Agreed. It's worth noting that AssertionError isn't affected by the -O flag - only the assert *statement*. Also, anything that says "except AssertionError:" (outside of unit testing) should be considered a major bug, which in turn means that this should *only* be raised when you truly expect that normal usage cannot ever hit this. Which is perfect for the use-case you describe. ChrisA

On 10Apr2019 23:09, Stefano Borini <stefano.borini@gmail.com> wrote:
As mentioned, AssertionErrors are good for this. I also use the icontract PyPI library, which provides decorators for annotating functions with preconditions and postconditions, and which are disabled in the same circumstances where assertions are disabled. It produces nice exception messages, too, aiding debugging. Also, it inspects the function definition, and so your preconditions use the same parameter names as in the function header.
That I tend to use RuntimeError for. I accept that my criteria for this difference are nebulous. I think in my mind: from icontract import require @require(lambda s: s in ('a', 'b', 'c')) def f(s, z): if s == 'a': ... elif s == 'b': ... else: raise RuntimeError("valid input s=%r, but unhandled!" % s) The @require is an assertion that the _caller_ used us correctly. The RuntimeError means that _I_, the function implementor, have screwed up right here instead of in the larger programme. Cheers, Cameron Simpson <cs@cskk.id.au>

That's quite a good idea, but then I think it should be more explicit in the documentation that the purpose goes beyond the assert statement failure. I've never seen AssertionError raised manually. On Wed, 10 Apr 2019 at 23:15, Jeroen Demeyer <J.Demeyer@ugent.be> wrote:
-- Kind regards, Stefano Borini

On Thu, Apr 11, 2019 at 8:15 AM Jeroen Demeyer <J.Demeyer@ugent.be> wrote:
Agreed. It's worth noting that AssertionError isn't affected by the -O flag - only the assert *statement*. Also, anything that says "except AssertionError:" (outside of unit testing) should be considered a major bug, which in turn means that this should *only* be raised when you truly expect that normal usage cannot ever hit this. Which is perfect for the use-case you describe. ChrisA

On 10Apr2019 23:09, Stefano Borini <stefano.borini@gmail.com> wrote:
As mentioned, AssertionErrors are good for this. I also use the icontract PyPI library, which provides decorators for annotating functions with preconditions and postconditions, and which are disabled in the same circumstances where assertions are disabled. It produces nice exception messages, too, aiding debugging. Also, it inspects the function definition, and so your preconditions use the same parameter names as in the function header.
That I tend to use RuntimeError for. I accept that my criteria for this difference are nebulous. I think in my mind: from icontract import require @require(lambda s: s in ('a', 'b', 'c')) def f(s, z): if s == 'a': ... elif s == 'b': ... else: raise RuntimeError("valid input s=%r, but unhandled!" % s) The @require is an assertion that the _caller_ used us correctly. The RuntimeError means that _I_, the function implementor, have screwed up right here instead of in the larger programme. Cheers, Cameron Simpson <cs@cskk.id.au>
participants (5)
-
Cameron Simpson
-
Chris Angelico
-
Elazar
-
Jeroen Demeyer
-
Stefano Borini