@@ -40,13 +40,190 @@ A trivial example of using the api is the following
4040 Extending mypy using plugins
4141****************************
4242
43+ Python is a highly dynamic language and has extensive metaprogramming
44+ capabilities. Many popular libraries use these to create APIs that may
45+ be more flexible and/or natural for humans, but are hard to express using
46+ static types. Extending the PEP 484 type system to accommodate all existing
47+ dynamic patterns is impractical and often just impossible.
48+
4349Mypy supports a plugin system that lets you customize the way mypy type checks
4450code. This can be useful if you want to extend mypy so it can type check code
45- that uses a library that is difficult to express using just PEP 484 types, for
46- example.
51+ that uses a library that is difficult to express using just PEP 484 types.
52+
53+ The plugin system is focused on improving mypy's understanding
54+ of *semantics * of third party frameworks. There is currently no way to define
55+ new first class kinds of types.
56+
57+ .. note ::
58+
59+ The plugin system is experimental and prone to change. If you want to write
60+ a mypy plugin, we recommend you start by contacting the mypy core developers
61+ on `gitter <https://gitter.im/python/typing >`_. In particular, there are
62+ no guarantees about backwards compatibility. Backwards incompatible changes
63+ may be made without a deprecation period.
64+
65+ Configuring mypy to use plugins
66+ *******************************
67+
68+ Plugins are Python files that can be specified in a mypy
69+ :ref: `config file <config-file >` using one of the two formats: relative or
70+ absolute path to the plugin to the plugin file, or a module name (if the plugin
71+ is installed using ``pip install `` in the same virtual environment where mypy
72+ is running). The two formats can be mixed, for example:
73+
74+ .. code-block :: ini
75+
76+ [mypy]
77+ plugins = /one/plugin.py, other.plugin
78+
79+ Mypy will try to import the plugins and will look for an entry point function
80+ named ``plugin ``. If the plugin entry point function has a different name, it
81+ can be specified after colon:
82+
83+ .. code-block :: ini
84+
85+ [mypy]
86+ plugins = custom_plugin:custom_entry_point
87+
88+ In following sections we describe basics of the plugin system with
89+ some examples. For more technical details please read docstrings in
90+ `mypy/plugin.py <https://github.com/python/mypy/blob/master/mypy/plugin.py >`_
91+ in mypy source code. Also you can find good examples in the bundled plugins
92+ located in `mypy/plugins <https://github.com/python/mypy/tree/master/mypy/plugins >`_.
93+
94+ High-level overview
95+ *******************
96+
97+ Every entry point function should accept a single string argument
98+ that is a full mypy version and return a subclass of ``mypy.plugins.Plugin ``:
99+
100+ .. code-block :: python
101+
102+ from mypy.plugin import Plugin
103+
104+ class CustomPlugin (Plugin ):
105+ def get_type_analyze_hook (self , fullname : str ):
106+ # see explanation below
107+ ...
108+
109+ def plugin (version : str ):
110+ # ignore version argument if the plugin works with all mypy versions.
111+ return CustomPlugin
112+
113+ During different phases of analyzing the code (first in semantic analysis,
114+ and then in type checking) mypy calls plugin methods such as
115+ ``get_type_analyze_hook() `` on user plugins. This particular method for example
116+ can return a callback that mypy will use to analyze unbound types with given
117+ full name. See full plugin hook methods list :ref: `below <plugin-hooks >`.
118+
119+ Mypy maintains a list of plugins it gets from the config file plus the default
120+ (built-in) plugin that is always enabled. Mypy calls a method once for each
121+ plugin in the list until one of the methods returns a non-``None `` value.
122+ This callback will be then used to customize the corresponding aspect of
123+ analyzing/checking the current abstract syntax tree node.
124+
125+ The callback returned by the ``get_xxx `` method will be given a detailed
126+ current context and an API to create new nodes, new types, emit error messages
127+ etc., and the result will be used for further processing.
128+
129+ Plugin developers should ensure that their plugins work well in incremental and
130+ daemon modes. In particular, plugins should not hold global state due to caching
131+ of plugin hook results.
132+
133+ .. _plugin_hooks :
134+
135+ Current list of plugin hooks
136+ ****************************
137+
138+ **get_type_analyze_hook() ** customizes behaviour of the type analyzer.
139+ For example, PEP 484 doesn't support defining variadic generic types:
140+
141+ .. code-block :: python
142+
143+ from lib import Vector
144+
145+ a: Vector[int , int ]
146+ b: Vector[int , int , int ]
147+
148+ When analyzing this code, mypy will call ``get_type_analyze_hook("lib.Vector") ``,
149+ so the plugin can return some valid type for each variable.
150+
151+ **get_function_hook() ** is used to adjust the return type of a function call.
152+ This is a good choice if the return type of some function depends on *values *
153+ of some arguments that can't be expressed using literal types (for example
154+ a function may return an ``int `` for positive arguments and a ``float `` for
155+ negative arguments). This hook will be also called for instantiation of classes.
156+ For example:
157+
158+ .. code-block :: python
159+
160+ from contextlib import contextmanager
161+ from typing import TypeVar, Callable
162+
163+ T = TypeVar(' T' )
164+
165+ @contextmanager # built-in plugin can infer a precise type here
166+ def stopwatch (timer : Callable[[], T]) -> Iterator[T]:
167+ ...
168+ yield timer()
169+
170+ **get_method_hook() ** is the same as ``get_function_hook() `` but for methods
171+ instead of module level functions.
172+
173+ **get_method_signature_hook() ** is used to adjust the signature of a method.
174+ This includes special Python methods except ``__init__() `` and ``__new__() ``.
175+ For example in this code:
176+
177+ .. code-block :: python
178+
179+ from ctypes import Array, c_int
180+
181+ x: Array[c_int]
182+ x[0 ] = 42
183+
184+ mypy will call ``get_method_signature_hook("ctypes.Array.__setitem__") ``
185+ so that the plugin can mimic the ``ctypes `` auto-convert behavior.
186+
187+ **get_attribute_hook ** can be used to give more precise type of an instance
188+ attribute. Note however, that this method is only called for variables that
189+ already exist in the class symbol table. If you want to add some generated
190+ variables/methods to the symbol table you can use one of the three hooks
191+ below.
192+
193+ **get_class_decorator_hook() ** can be used to update class definition for
194+ given class decorators. For example, you can add some attributes to the class
195+ to match runtime behaviour:
196+
197+ .. code-block :: python
198+
199+ from lib import customize
200+
201+ @customize
202+ class UserDefined :
203+ pass
204+
205+ var = UserDefined
206+ var.customized # mypy can understand this using a plugin
207+
208+ **get_metaclass_hook() ** is similar to above, but for metaclasses.
209+
210+ **get_base_class_hook() ** is similar to above, but for base classes.
211+
212+ **get_dynamic_class_hook() ** can be used to allow dynamic class definitions
213+ in mypy. This plugin hook is called for every assignment to a simple name
214+ where right hand side is a function call:
215+
216+ .. code-block :: python
217+
218+ from lib import dynamic_class
219+
220+ X = dynamic_class(' X' , [])
47221
48- *Warning: * The plugin system is extremely experimental and prone to change. If you want
49- to contribute a plugin to mypy, we recommend you start by contacting the mypy
50- core developers either on `gitter <https://gitter.im/python/typing >`_ or on mypy's
51- `issue tracker <https://github.com/python/mypy/issues >`_.
222+ For such definition, mypy will call ``get_dynamic_class_hook("lib.dynamic_class") ``.
223+ The plugin should create the corresponding ``mypy.nodes.TypeInfo `` object, and
224+ place it into a relevant symbol table. (Instances of this class represent
225+ classes in mypy and hold essential information such as qualified name,
226+ method resolution order, etc.)
52227
228+ **get_customize_class_mro_hook() ** can be used to modify class MRO (for example
229+ insert some entries there) before the class body is analyzed.
0 commit comments