On Fri, 25 Sep 2020 at 15:57, Paul Moore <p.f.moore@gmail.com> wrote:
>
> On Fri, 25 Sep 2020 at 14:15, Chris Angelico <rosuav@gmail.com> wrote:
>
> > Why? Do you really think you can enumerate EVERY possible way that
> > something might fail?
>
> Rust does a surprisingly good job of that, actually. But the point is
> that Python is not Rust, and the infrastructure Rust uses to allow it
> to manage code safety is baked into the language core at a very
> fundamental level.
>
> Enumerating the exceptions that a piece of code can raise is
> impractical and unhelpful in Python. But that may not be immediately
> obvious to someone coming from a different language. That's why it's
> important to understand Python properly before proposing new features
> that work in other languages. (I don't think that's what the OP is
> doing here, to be clear, but the discussion is drifting in that
> direction, with Rust's Result type having been mentioned).
>
> **In Python**, writing code from the perspective of "what can I handle
> at this point" is the right approach. Deferring unexpected exceptions
> to your caller is the default behaviour, and results in a clean,
> natural style *for Python*. The proposal here is basically in direct
> contradiction to that style.
I do agree but maybe that suggests a different role for annotated
exceptions in Python. Rather than attempting to enumerate all possible
exceptions annotations could be used to document in a statically
analysable way what the "expected" exceptions are. A type checker
could use those to check whether a caller is handling the *expected*
exceptions rather than to verify that the list of *all* exceptions
possibly raised is exhaustive.
Consider an example:
def inverse(M: Matrix) -> Matrix: raises(NotInvertibleError)
if determinant(M) == 0:
raise NotInvertibleError
rows, cols = M.shape
for i in range(rows):
for j in range(cols):
...
Here the function is expected to raise NotInvertibleError for some
inputs. It is also possible that the subsequent code could raise an
exception e.g. AttributeError, TypeError etc and it's not necessarily
possible to enumerate or exhaustively rule out what those
possibilities might be. If we wanted to annotate this with
raises(NotInvertibleError) then it would be very hard or perhaps
entirely impossible for a type checker to verify that no other
exception can be raised. Or maybe even the type checker could easily
come up with a large list of possibilities that you would never want
to annotate your code with. Maybe that's not what the purpose of the
annotation is though.
What the type checker can do is check whether a caller of this
function handles NotInvertibleError after seeing the *explicit* type
hint. A function that calls inverse without catching the exception can
also be considered as raises(NotInvertibleError). You might want to
enforce in your codebase that the caller should catch and suppress the
expected exception or should itself have a compatible raises
annotation indicating that it can also be expected to raise the same
exception e.g. either of these is fine:
def some_calc(M: Matrix): raises(NotInvertibleError)
A = inverse(M)
...
def some_calc(M: Matrix):
try:
A = inverse(M)
except NotInvertibleError
# do something else
...
Perhaps rather than requiring all exceptions to be annotated
everywhere you could allow the raises type hints to propagate
implicitly and only verify them where there is another explicit type
hint:
def some_calc(M):
# no hint but checker infers this raises NotInvertibleError
A = inverse(M)
def other_func(M): raises(ZeroError)
# checker gives an error for this
# because the raises should include NotInvertibleError
B = some_calc(M)
You could then have an explicit hint for the type checker to say that
a function is not expected to raise any exceptions maybe like this:
def main(args): raises(None)
...
The intent of this would be that the type checker could then follow
the chain of all functions called by main to verify that any
exceptions that were expected to raise had been handled somewhere.
This wouldn't verify all of the exceptions that could possibly be
raised by any line of code. It could verify that for those exceptions
that have been explicitly annotated.
Oscar
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/L2YK75C7XSFWOJLM6ROAI3ZVAY2WE5GZ/
Code of Conduct: http://python.org/psf/codeofconduct/