Python Type Hints - How to Debug Types With reveal_type()

2021-05-14 Eye see what type you have there.

When working with type hints, it’s often useful to debug the types of variables. Type checkers allow you to do this with reveal_type() and reveal_locals().

For example, take this code:

items = [1, None]

We don’t need to add a hint to items since our type checker can infer its type. But we might not be sure what type has been inferred—is it list[int | None], the less useful list[object], or worst list[Any]? We can check by adding a call to reveal_type():

items = [1, None]
reveal_type(items)

Then when we run our type checker, in this case Mypy, it will log the type of items:

$ mypy example.py
example.py:2: note: Revealed type is 'builtins.list[Union[builtins.int, None]]'

Here Mypy wrote its “long-form spelling” of list[int | None] (in a future version it may use a shorter form).

Note that reveal_type() does not exist at runtime, so if we run our code with the call in place, Python crashes with a NameError:

$ python example.py
Traceback (most recent call last):
  File "/.../example.py", line 2, in <module>
    reveal_type(items)
NameError: name 'reveal_type' is not defined

So, we need to remove all calls to reveal_type() before running our code.

A good way to make sure you never accidentally commit a call to reveal_type() is to use flake8, perhaps under pre-commit. It will flag calls with the error F821 undefined name 'reveal_type'.

Also, reveal_locals()

Mypy also supports reveal_locals(), which does a reveal_type() for each local variable. This can save time when debugging several variables.

For example, if we take this code:

CONSTANT = 1


def example() -> None:
    first_item = 1
    items = [first_item, None]
    reveal_locals()

We can run Mypy and see:

$ mypy example.py
example.py:7: note: Revealed local types are:
example.py:7: note:     first_item: builtins.int
example.py:7: note:     items: builtins.list[Union[builtins.int, None]]

The local variables first_item and items have their types revealed, but the global variable CONSTANT does not.

Fin

—Adam


Want better tests? Check out my book Speed Up Your Django Tests which teaches you to write faster, more accurate tests.


Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: mypy, python