Skip to content

Implementation Overview

Michael Lee edited this page May 16, 2018 · 16 revisions

This is a general introduction to the mypy implementation. The linked subpages have more detailed information. The file links point to the mypy git repository.

Structure of the type checker

The main entry point of mypy is mypy/main.py. This file is responsible for parsing the config file, parsing command line options, and handling other related boilerplate.

The flow of execution is then passed off to the Build Manager (mypy/build.py), which is responsible for coordinating a build, managing dependencies between modules, and performing compilation passes in the correct order.

The build manager will then perform several passes over each module to typecheck them (see the process_graph function for more details).

Common passes:

  1. Python Parser (mypy/parse.py, mypy/fastparse.py, mypy/fastparse2.py)
    • Uses the typed_ast to build an abstract syntax tree (AST). The typed_ast library is nearly identical to CPython's "ast" module except that it has better support for parsing things like type comments.
    • This AST is then converted into mypy-specific parse tree, which is defined in the files mypy/nodes.py and mypy/types.py
  2. Semantic Analyzer (mypy/semanal.py, mypy/semanal_*.py, mypy/typeanal.py)
    • Binds names to definitions
    • Performs various consistency checks
  3. Type Checker (mypy/checker.py, mypy/checkexpr.py, mypy/checkmember.py)
    • Type checks the program
    • Performs type inference

Tests

NOTE: We're moving away from runtests.py and towards pytest. But the migration isn't finished yet. Up to date docs for tests are in the repo at test-data/unit/README.md. The docs below are out of date and unmaintained.

The file runtests.py runs all the tests. Test suites for individual components are in the files mypy/test/test*.py. You can run many of these individually by doing runtests.py testfoobar. Type checker test cases (testcheck.py) have been migrated to pytest and you can run them using py.test mypy. (We are planning to migrate all test cases to pytest eventually -- any help is appreciated here.)

Many test suites store test case descriptions in text files (test-data/unit/*.test). The module mypy.test.data parses these descriptions. The package mypy.myunit contains the test framework used for the non-checker test cases. The unit tests use minimal stubs for builtins, so a lot of operations are not possible. You should generally define any needed classes within the test case instead of relying on builtins, though clearly this is not always an option (see below for more about stubs in test cases). This way tests run much faster and don't break if the stubs change. If your test crashes mysteriously even though the code works when run manually, you should make sure you have all the stubs you need for your test case, including built-in classes such as list or dict, as these are not included by default.

Notes about test cases:

  • Python evaluation test cases are a little different from unit tests (mypy/test/testpythoneval.py, test-data/unit/pythoneval.test). These type check programs and run them. Unlike the unit tests, these use the full builtins and library stubs instead of minimal ones. Run them using runtests.py testpythoneval.

More things (this should be expanded):

  • You can run just a subset of tests by giving a wildcard argument, e.g. runtests.py unit-test -a "*Overload*" to run all unit tests with Overload as a substring.
  • The builtins used by default in unit tests live in test-data/unit/lib-stub. Individual tests cases can override the stubs by using [builtins fixtures/foo.py]; this targets files in test-data/unit/fixtures. You are free add additional stubs to this directory, but generally don't update files in lib-stub without first discussing the addition with other mypy developers, as additions could slow down the test suite.