Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 36fcf4f

Browse files
authored
Add plugin docs (#6057)
This adds documentation for the plugin system. To avoid duplication, I add shorter and more basic info to the docs, while adding more technical details to the module/class/method docstrings.
1 parent dddf7fa commit 36fcf4f

2 files changed

Lines changed: 411 additions & 22 deletions

File tree

docs/source/extending_mypy.rst

Lines changed: 183 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
4349
Mypy supports a plugin system that lets you customize the way mypy type checks
4450
code. 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

Comments
 (0)