Plugin system for extending mypy.
At large scale the plugin system works as following:
get_xxx
methods on user plugins with a single string argument that
is a fully qualified name (full name) of a relevant definition
(see mypy.plugin.Plugin method docstrings for details).XxxContext
named tuples for details about which information is given to each hook.Plugin developers should ensure that their plugins work well in incremental and daemon modes. In particular, plugins should not hold global state, and should always call add_plugin_dependency() in plugin hooks called during semantic analysis. See the method docstring for more details.
There is no dedicated cache storage for plugins, but plugins can store per-TypeInfo data in a special .metadata attribute that is serialized to the mypy caches between incremental runs. To avoid collisions between plugins, they are encouraged to store their state under a dedicated key coinciding with plugin name in the metadata dictionary. Every value stored there must be JSON-serializable.
## Notes about the semantic analyzer
Mypy 0.710 introduced a new semantic analyzer that changed how plugins are expected to work in several notable ways (from mypy 0.730 the old semantic analyzer is no longer available):
The order of processing AST nodes in modules is different. The old semantic analyzer processed modules in textual order, one module at a time. The new semantic analyzer first processes the module top levels, including bodies of any top-level classes and classes nested within classes. ("Top-level" here means "not nested within a function/method".) Functions and methods are processed only after module top levels have been finished. If there is an import cycle, all module top levels in the cycle are processed before processing any functions or methods. Each unit of processing (a module top level or a function/method) is called a target.
This also means that function signatures in the same module have not been
analyzed yet when analyzing the module top level. If you need access to
a function signature, you'll need to explicitly analyze the signature first
using anal_type()
.
Each target can be processed multiple times. This may happen if some forward references are not ready yet, for example. This means that semantic analyzer related plugin hooks can be called multiple times for the same full name. These plugin methods must thus be idempotent.
The anal_type
API function returns None if some part of the type is not
available yet. If this happens, the current target being analyzed will be
deferred, which means that it will be processed again soon, in the hope
that additional dependencies will be available. This may happen if there are
forward references to types or inter-module references to types within an
import cycle.
Note that if there is a circular definition, mypy may decide to stop
processing to avoid an infinite number of iterations. When this happens,
anal_type
will generate an error and return an AnyType
type object
during the final iteration (instead of None).
There is a new API method defer()
. This can be used to explicitly request
the current target to be reprocessed one more time. You don't need this
to call this if anal_type
returns None, however.
There is a new API property final_iteration
, which is true once mypy
detected no progress during the previous iteration or if the maximum
semantic analysis iteration count has been reached. You must never
defer during the final iteration, as it will cause a crash.
The node
attribute of SymbolTableNode objects may contain a reference to
a PlaceholderNode object. This object means that this definition has not
been fully processed yet. If you encounter a PlaceholderNode, you should
defer unless it's the final iteration. If it's the final iteration, you
should generate an error message. It usually means that there's a cyclic
definition that cannot be resolved by mypy. PlaceholderNodes can only refer
to references inside an import cycle. If you are looking up things from
another module, such as the builtins, that is outside the current module or
import cycle, you can safely assume that you won't receive a placeholder.
When testing your plugin, you should have a test case that forces a module top level to be processed multiple times. The easiest way to do this is to include a forward reference to a class in a top-level annotation. Example:
c: C # Forward reference causes second analysis pass class C: pass
Note that a forward reference in a function signature won't trigger another pass, since all functions are processed only after the top level has been fully analyzed.
You can use api.options.new_semantic_analyzer
to check whether the new
semantic analyzer is enabled (it's always true in mypy 0.730 and later).
Class | ChainedPlugin |
A plugin that represents a sequence of chained plugins. |
Class | CheckerPluginInterface |
Interface for accessing type checker functionality in plugins. |
Class | CommonPluginApi |
A common plugin API (shared between semantic analysis and type checking phases) that all plugin hooks get independently of the context. |
Class | Plugin |
Base class of all type checker plugins. |
Class | SemanticAnalyzerPluginInterface |
Interface for accessing semantic analyzer functionality in plugins. |
Class | TypeAnalyzerPluginInterface |
Interface for accessing semantic analyzer functionality in plugins. |
Constant | T |
Undocumented |
Variable | AnalyzeTypeContext |
Undocumented |
Variable | AttributeContext |
Undocumented |
Variable | ClassDefContext |
Undocumented |
Variable | DynamicClassDefContext |
Undocumented |
Variable | FunctionContext |
Undocumented |
Variable | FunctionSigContext |
Undocumented |
Variable | MethodContext |
Undocumented |
Variable | MethodSigContext |
Undocumented |
Variable | ReportConfigContext |
Undocumented |