Mark Amery Mark Amery - 1 year ago 76
Python Question

Type hint that a function never returns

Python's new type hinting feature allows us to type hint that a function returns


def some_func() -> None:

... or to leave the return type unspecified, which the PEP dictates should cause static analysers to assume that any return type is possible:

Any function without annotations should be treated as having the most general type possible

However, how should I type hint that a function will never return? For instance, what is the correct way to type hint the return value of these two functions?

def loop_forever():
while True:
print('This function never returns because it loops forever')

def always_explode():
raise Exception('This function never returns because it always raises')

Neither specifying
-> None
nor leaving the return type unspecified seems correct in these cases.

Answer Source

There is no answer to this question, yet. Here are a couple of reasons:

  • When a function doesn't return, there is no return value (not even None) that a type could be assigned to. So you are not actually trying to annotate a type; you are trying to annotate the absence of a type.

  • The type hinting PEP has only just been adopted in the standard, as of Python version 3.5. In addition, the PEP only advises on what type annotations should look like, while being intentionally vague on how to use them. So there is no standard telling us how to do anything in particular, beyond the examples.

  • The PEP has a section Acceptable type hints stating the following:

    Annotations must be valid expressions that evaluate without raising exceptions at the time the function is defined (but see below for forward references).

    Annotations should be kept simple or static analysis tools may not be able to interpret the values. For example, dynamically computed types are unlikely to be understood. (This is an intentionally somewhat vague requirement, specific inclusions and exclusions may be added to future versions of this PEP as warranted by the discussion.)

    So it tries to discourage you from doing overly creative things, like throwing an exception inside a return type hint in order to signal that a function never returns.

  • Regarding exceptions, the PEP states the following:

    No syntax for listing explicitly raised exceptions is proposed. Currently the only known use case for this feature is documentational, in which case the recommendation is to put this information in a docstring.

  • There is a recommendation on type comments, in which you have more freedom, but even that section doesn't discuss how to document the absence of a type.

There is one thing you could try in a slightly different situation, when you want to hint that a parameter or a return value of some "normal" function should be a callable that never returns. The syntax is Callable[[ArgTypes...] ReturnType], so you could just omit the return type, as in Callable[[ArgTypes...]]. However, this doesn't conform to the recommended syntax, so strictly speaking it isn't an acceptable type hint. Type checkers will likely choke on it.

Conclusion: you are ahead of your time. This may be disappointing, but there is an advantage for you, too: you can still influence how non-returning functions should be annotated. Maybe this will be an excuse for you to get involved in the standardisation process. :-)

I have two suggestions.

  1. Allow omitting the return type in a Callable hint and allow the type of anything to be forward hinted. This would result in the following syntax:

    always_explode: Callable[[]]
    def always_explode():
        raise Exception('This function never returns because it always raises')
  2. Introduce a bottom type like in Haskell:

    def always_explode() -> ⊥:
        raise Exception('This function never returns because it always raises')

These two suggestions could be combined.