Skip to main content

Integrating mypy into another Python application

You can integrate mypy into another Python 3 application by importing mypy.api and calling the run function with a list of command line arguments.

Basic integration

The run function returns a tuple of (normal_report, error_report, exit_status):
import sys
from mypy import api

result = api.run(sys.argv[1:])

if result[0]:
    print('\nType checking report:\n')
    print(result[0])  # stdout

if result[1]:
    print('\nError report:\n')
    print(result[1])  # stderr

print('\nExit status:', result[2])
The run function accepts the same arguments that mypy would accept on the command line.

Extending mypy using plugins

Python’s dynamic nature and metaprogramming capabilities make it challenging to express all patterns using static types. Mypy’s plugin system lets you customize type checking for libraries that use dynamic APIs.
The plugin system is experimental and subject to change. Breaking changes may occur without deprecation. Check the plugin API changes announcement for updates.

Why use plugins?

Plugins are useful when:
  • A library uses metaprogramming that’s hard to express with PEP 484 types
  • You need to extend mypy’s understanding of third-party framework semantics
  • You want to customize type checking behavior for specific patterns
Plugins focus on improving semantic understanding. You cannot define new first-class types.

Configuring mypy to use plugins

Plugins are specified in your mypy configuration file using the plugins option:
[mypy]
plugins = /one/plugin.py, other.plugin
You can specify plugins as:
  • Relative or absolute paths to Python files
  • Module names (if installed via pip in the same environment)

Custom entry points

If your plugin uses a different entry point function name:
[mypy]
plugins = custom_plugin:custom_entry_point

High-level overview

Every plugin entry point function accepts a mypy version string and returns a Plugin subclass:
from mypy.plugin import Plugin

class CustomPlugin(Plugin):
    def get_type_analyze_hook(self, fullname: str):
        # Customize type analysis for specific names
        if fullname == "mylib.SpecialType":
            return my_analyze_special_type
        return None

def plugin(version: str):
    # Ignore version if plugin works with all mypy versions
    return CustomPlugin
1
Step 1: Plugin registration
2
Mypy collects plugins from the config file and imports them.
3
Step 2: Hook method calls
4
During semantic analysis and type checking, mypy calls hook methods like get_type_analyze_hook() on each plugin.
5
Step 3: First match wins
6
Mypy calls each plugin’s method until one returns a non-None callback.
7
Step 4: Callback execution
8
The callback receives context and API access to create nodes, types, and emit errors.

Plugin development guidelines

Plugins must work well in incremental and daemon modes:
  • Do not hold global state (results are cached)
  • Always call add_plugin_dependency() during semantic analysis
  • Make hooks idempotent (they may be called multiple times)

Incremental mode considerations

Plugins can store per-TypeInfo data in the .metadata attribute, which is serialized between runs:
def my_class_hook(ctx: ClassDefContext) -> None:
    # Store plugin state under a unique key
    ctx.cls.info.metadata['my_plugin'] = {
        'processed': True,
        'custom_data': 'some value'
    }
Use your plugin name as the metadata key to avoid collisions. All values must be JSON-serializable.

Examples from bundled plugins

Mypy includes several built-in plugins you can reference:
  • dataclasses - Adds __init__ and other methods to @dataclass decorated classes
  • attrs - Supports the attrs library
  • ctypes - Handles ctypes array types
  • enums - Provides better enum support
Find plugin examples in the mypy/plugins directory.

Testing your plugin

Always test that your plugin handles multiple analysis passes:
# This forward reference forces a second analysis pass
c: C  # Forward reference causes second analysis pass
class C: pass
Forward references in function signatures don’t trigger another pass since functions are processed after the module top level.

Next steps