Earlier this month, mypy released version 1.0.

In case you were not aware, python allows you to specify "type hints" whereby, much like in static typed languages, you can declare the data type of a variable. In fact, you may be surprised to know that Python's type hint system is much more expressive and powerful than popular static typed languages like Java!

Now, python runtime itself ignores the type hints completely. You can declare a variable as int and assign it to a str and python will have no problem with it.

What use are the type hints then? Well, you can use other tools – called type checkers – to scan the code and verify that it is compliant to the type declarations.

One such tool is mypy.

Now, mypy has been production ready for years. But still, the version 1.0 release is significant. To celebrate this, we are going to be doing a series of articles on the various facets of Python's type hinting feature.

Setting up mypy

The first thing we need to do is install mypy. Simply run pip install mypy  and you are good to go. pip will download and install mypy.

Now that we have mypy setup, take a look at the code below.

a: int = 5
b: str = "hello"
c: int = "10"

Here we see the basic type hinting syntax. Just put a colon after the variable name and declare the type of the variable there.

You might have noticed that the third variable c is declared as an int but we assign a string to it. As we discussed earlier in the article, python has no issues with that. It completely ignores type hints and the code will run without any errors just fine.

Now lets run this code through mypy

> mypy test.py

test.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
Found 1 error in 1 file (checked 1 source file)

As we can see, mypy validates the types and raises an error on line 3, complaining of incompatible types between what was declared and what was assigned.

So this is the general workflow when using type hints. You regularly run mypy and it will use type hints in the code to catch type errors.

In fact most popular IDEs are preconfigured to use mypy if it is installed, and some like PyCharm and VSCode ship with their own type checkers as well. Even text editors like Vim and NeoVim can be integrated with mypy so that the type errors are shown in the editor while you are coding. A proper configuration makes it easy to catch and fix the errors during coding, rather than discover the mistake during runtime.

Type hints for functions

In the previous code we saw how we can add type hints for variables. Now lets take a look at functions.

def add(a:int, b:int) -> int:
    return a + b
    
c = add(20, 3)
print(c)

Type hinting the arguments follows that for variables: add a colon followed by the type hint.

You can also declare what type the function will return. After the closing parenthesis add a -> (thats a hyphen followed by greater that to form an arrow) followed by the return type. The final colon goes after the return type declaration.

Now mypy will complain if you try to pass anything other than int to the add function. Whats more, mypy has type inferencing, so it can figure out that c is an int even though we did not declare any type for c. mypy is very smart! 🤓

💡
Type Inferencing: Type inferencing is a feature of type checkers where they can follow the type declarations in one part of the code to figure out the types of other variables. In the example above, mypy knows from the type hint that the add function returns an int. Using this knowledge it can infer that the type of variable c must also be an int.

Modules and Typeshed

Due to type inferencing, when you use a function or module that has type hints, other parts of your code that do not contain type hints will get their type inferred and you can get a lot of benefits of the type hinting workflow.

For this reason, it is highly recommended to add type hints to function or module APIs. In fact, many third party modules already have type hints. mypy will catch errors when calling those functions, even if you don't use type hints in your own code.

Some third party modules are not type hinted. But don't worry. Python has a typeshed repository which contains type hints for many such modules.

GitHub - python/typeshed: Collection of library stubs for Python, with static types
Collection of library stubs for Python, with static types - GitHub - python/typeshed: Collection of library stubs for Python, with static types

Let's take an example. The code below uses the requests module. This is one of the most popular modules for making HTTP requests. Unfortunately, the requests does not  use python's type hinting feature. There are some bugs in the code below, but mypy can't find them without the type hints.

import requests

response = requests.get('https://www.google.co.in',
                        headers='Accepts: text/html')
status = response.status_code
print('Status code: ' + status)

Fortunately, the community has contributed type hints for the module and those type hints are avaialable in the typeshed. To get the type declarations from the typeshed, we need to install the types-requests package. Just pip install types-requests and mypy is good to go.

Here is what mypy says about our code now

> mypy test.py

test.py:3: error: Argument "headers" to "get" has incompatible type "str"; expected "Optional[Mapping[str, Union[str, bytes]]]" [arg-type]
test.py:5: error: Unsupported operand types for + ("str" and "int") [operator]
Found 2 errors in 1 file (checked 1 source file)

First, it points out that the headers argument to requests.get() does not support a string type. It needs to be a dict.

Furthermore, the type declaration says that the status_code field is an int, and using type inferencing, mypy knows that the status variable is also an int. mypy uses that knowledge to flag an error on the next line: You cannot use the + operator to add a str and int.

This is a good example of how using a type hinted module can uncover bugs in our code, even when we don't use typing ourselves. You shoud check if the modules you use have type hints (many do!) and if not, whether typeshed provides type hints for the module.

Moral of the story: Type inference is a super powerful feature 🎉

Summary

So thats the basics of getting started with the type hinting feature in python. If you write python code of any considerable size, type hinting should 💯 be a part of your workflow. Why catch bugs in production when you can catch them in your editor?

And if you thought it is too much effort to adopt type hints, fear not! With type inferencing you can get good benefits without any needing to change any code. You can then incrementally add hints to your own code over time.

Now that we know how to use mypy, from the next article of the series we will start exploring the type system available in python.

Did you like this article?

If you liked this article, consider subscribing to this site. Subscribing is free.

Why subscribe? Here are three reasons:

  1. You will get every new article as an email in your inbox, so you never miss an article
  2. You will be able to comment on all the posts, ask questions, etc
  3. Once in a while, I will be posting conference talk slides, longer form articles (such as this one), and other content as subscriber-only

Tagged in: