Wednesday 2 May 2018

Mypy 0.600 Released

We’ve just uploaded mypy 0.600 to the Python Package Index (PyPI). Mypy is an optional static type checker for Python. This is a major release that turns on strict optional checking by default (there’s a flag to disable it for backward compatibility), introduces a new mypy daemon mode with much, much faster incremental checking, along with other new features, bug fixes and library stub (typeshed) updates. You can install it as follows:

    python3 -m pip install -U mypy

You can read the documentation for this release on ReadTheDocs.

New Features

Strict Optional Checking Enabled by Default

Over a year after introducing it, strict optional mode is now on by default. This means that mypy checks that you use an Optional[…] type whenever a None is a valid value (see below for how to get the old behavior):

    def greet(name: str) -> None:
        print('hi, ' + name)

    greet(None)  # Error! None not compatible with 'str'

Mypy also verifies that only valid operations are performed on None values:

    from typing import Optional

    def greet(name: Optional[str]) -> None:
        print('hi, ' + name)  # Error: Incompatible types
                              # 'str' and 'Optional[str]'

    greet(None)  # OK

Mypy recognizes common Python idioms for checking against None values:

    from typing import Optional

    def greet(name: Optional[str]) -> None:
        if name is None:
            print('hello, stranger')
        else:
            print('hi, ' + name)  # OK

    greet(None)  # OK

You can still get the old behavior, where None was a valid value for every type, by using the --no-strict-optional flag (or strict_optional = False in your config file). Strict optional checking was previously available through the --strict-optional flag, which is no longer required. Read the now significantly expanded documentation for the details, including hints about how to migrate an existing codebase to strict optional checking. We have no plans to deprecate the backward compatibility flag, however.

Mypy Daemon

This release adds support for a mypy daemon (in beta) which can speed up mypy runtimes by a large factor, especially when performing incremental builds where only a few files have changed. The mypy daemon is a server process that runs on your development machine (where you’d normally run mypy) and keeps program state in memory to avoid redundant work when running mypy repeatedly. There’s a client tool dmypy that requests type checking results from the daemon.

We’ve used the mypy daemon at Dropbox for over a month now (it’s been under development much longer), and typical incremental builds for a codebase with millions of lines complete in a few seconds. Running mypy using the daemon can be dozens of times faster than non-daemon incremental mypy runs for large codebases. The documentation explains how to use the mypy daemon.

Remote Caching

We’ve documented how to use remote caching with mypy, where you set up a Continuous Integration build to pre-generate mypy cache files for each new commit in your repository, and mypy runs “warm up” the cache by downloading a pre-generated cache file to speed up type checking. This can speed up mypy runtimes by a large factor.

We’ve used remote caching at Dropbox together with the mypy daemon for over a month now, and our users are very happy with the results. A cold mypy run (when the daemon is not already running) for a codebase with millions of lines often takes less than a minute, including the time needed to download the remote cache data (over a fast network). Without remote caching typical runtimes were several minutes. Read the documentation for details on how to set up remote caching.

Support Repeated Assignment to Local Variable '_'

Mypy now allows unrestricted assignments to the underscore variable (_) within functions, so code like this will no longer generate errors:

    from typing import Tuple

    def get_item(n: int) -> Tuple[int, str, float]: ...

    def get_length(n : int) -> int:
        length, _, _ = get_item(n)  # OK!
        assert length > 0
        return length

User-specific Config File

You can now store common mypy configuration options in a .mypy.ini file in your home directory, and it will be automatically used by mypy. This was contributed by Alejandro Avilés (PR 4939).

Notable Bugs Fixed

  • Fix crashes in coverage reports (PR 4978)
  • Fix to functional Enums (Elazar Gershuni, PR 4942)
  • Allow incompatible overriding of __slots__ (PR 4890)
  • Make an override of a ClassVar implicitly a ClassVar (Carl Meyer, PR 4718)
  • Use EnumMeta instead of Enum to mark enum classes (Elazar Gershuni, PR 4319)

Other Improvements

  • Improve performance with large config files and adjust semantics of section patterns (PR 4894)
  • Improve performance when using custom source encodings (PR 4957)
  • Reduce mypy memory usage (PR 4902, PR 4904)

Acknowledgments

First of all, we’d like to thank our employer, Dropbox, for funding the mypy core team.

Thanks to all mypy contributors who contributed to this release:

  • Alejandro Avilés
  • Carl Meyer
  • Elazar Gershuni
  • Emil Hessman
  • Ethan Smith
  • Jelle Zijlstra

Additional thanks to all contributors to typeshed:

  • Aaron Miller
  • Andrew Gaul
  • Charles-Axel Dein
  • Chris Gavin
  • Eddie Schoute
  • Freek Dijkstra
  • Jelle Zijlstra
  • John Reese
  • Martin DeMello
  • NODA, Kai
  • rchen152
  • Semyon Proshev
  • Siva Chandra
  • Svend Sorensen
  • Tomas Krizek
— Jukka Lehtosalo, on behalf of the mypy team