So I wrote a function that sometimes doesn’t return i.e. it will raise an exception under certain conditions.
from typing import NoReturn
def foo(a: int) -> int|NoReturn:
if not is instance(a, int):
raise TypeError("a must be int")
return a*2
That raises error that | isn’t defined for NoReturn. Strangely I could do Union[int, NoReturn] but I’m assuming mypy doesn’t like that given the final comment on this answer
Am I doing an anti pattern here? Is it wrong to annotate that a function could raise an exception? If so wouldn’t it be useful to indicate what type of exceptions to possibly except them?
Edit: for some reason I’m remembering someone mentioning on a mailing list that annotating possible exceptions is pointless bc a function being invoked in the function in question (invoked by the invoked) could raise and it’s a rabbit hole and I guess that makes sense…maybe
I think you’re on the right track, NoReturn is not for exceptions being raised, it’s literally for functions that cannot ever return (which are uncommon if not rare).
In general, any function that calls Python code could raise literally any exception you have ever heard of, or even exceptions that didn’t exist before you called the function.
For example len(obj) is guaranteed to return an int, or raise TypeError, provided obj is a builtin. But as soon as obj is a custom Python class with a __len__ method, it could raise anything, deliberately or due to bugs.
Any function, builtin or not, could raise and not return if given invalid input. So every function with a return annotation should be read as “return this type, or raise”.
Since every function could, in principle, raise and not return, it would be a waste of time to annotate everything that could raise. But that is not the purpose of the NoReturn annotation:
So the purpose of NoReturn is to annotate functions that unconditionally always raise, or exit without either returning or raising.
(I guess it could also be used to annotate a function which never terminates.)
So Union[NoReturn, T] for some type T should be considered an error by mypy, I don’t know why it isn’t.
And given that unions with NoReturn should not be supported, there is no point in supporting the | operator.
Yeah, I’d agree with that. I’ve written a number of functions with infinite loops in them, intending to have them terminate with an exception at some point (most commonly KeyboardInterrupt or EOFError).
So Union[NoReturn, T] for some type T should be considered an error by mypy, I don’t know why it isn’t.
I’m assuming it is, given the SO post I linked. It is not an error in python unlike NoReturn|T, for some type T, which raises an error in python. Which seems inconsistent given | is just shorthand for Union
The | operator for types is still new. It was introduced in Python 3.10:
A new type union operator was introduced which enables the syntax X | Y . This provides a cleaner way of expressing ‘either type X or type Y’ instead of using typing.Union, especially in type hints.
Please next time copy the error message instead of describing it.
You can use this new operator in type hints with older versions of Python (as far as 3.7) if your type checker (mypy, pyright…) supports it. Just tell the Python interpreter to not evaluate the annotation expressions by putting this __future__ import at the beginning of a module: