annotation for the documentation differs from the annotation for the type checking

Viewed 45

I need some help how to write the type hints, and it is not purely technical.

As an example, imagine a get_state() function returning the current state as a string.

The documentation states the initialize() must be called first and the result of get_state() is undefined prior to initialization. Actually it returns None when uninitialized, but that is an implementation detail.

The annotation could be:

  1. get_state() -> str which is correct assuming a proper usage. I find it helpful from the developer's point of view, but mypy complains because it is clear that the return value could be also None.
  2. get_state() -> str|None which matches the reality the most, but may change in the future and it introduces mypy warnings everywhere the return value is used and is obviously expected to be a string there.
  3. get_state() -> Any which exactly matches the documented API, but is useless.

So, who is the main recipient of the information in the annotation? Is it the developer getting additional information when reading the code? Or is it the type checker tool like the mypy that tries to find possible problems?

2 Answers

Probably str|None is what you want.

In my view, type annotations in Python try to reap some of the same benefits that static type systems bring to languages that have them. Good examples of languages with strong static type systems are Haskell and Rust. In such languages type annotations can never overpromise, like would happen with get_state() -> str. So that possibility is ruled out. get_state() -> str|None happens to be what the code is capable of supporting, so that is one option, and the documentation should probably then reflect that as well. If the developers think that this return type is likely to change or be different on different systems then it could be reasonable to go for a type like Any, but that would also have implications for how this function should be used. If all you know about this function is that it could return Any(thing) then what exactly can you do with this value? You could test whether it is a string and then use it as a string, but is that the way recommended in the documentation? If yes then the Any type is reasonable, if not then not.

These annotations are mostly for the developer as they don't affect runtime. That said, the type checker is also there to make your life easier. So if any of these are making it harder for you don't have to use them...

And more practically, can you return an empty sting ('') instead of None?

Related