|
1 | 1 | """Plugin system for extending mypy.
|
2 | 2 |
|
3 | 3 | At large scale the plugin system works as following:
|
4 |
| -* Plugins are collected from the corresponding config option |
5 |
| - (either a paths to Python files, or installed Python modules) |
6 |
| - and imported using importlib |
| 4 | +
|
| 5 | +* Plugins are collected from the corresponding mypy config file option |
| 6 | + (either via paths to Python files, or installed Python modules) |
| 7 | + and imported using importlib. |
| 8 | +
|
7 | 9 | * Every module should get an entry point function (called 'plugin' by default,
|
8 |
| - but may be overridden in the config file), that should accept a single string |
9 |
| - argument that is a full mypy version (includes git commit hash for dev versions) |
10 |
| - and return a subclass of mypy.plugins.Plugin |
| 10 | + but may be overridden in the config file) that should accept a single string |
| 11 | + argument that is a full mypy version (includes git commit hash for dev |
| 12 | + versions) and return a subclass of mypy.plugins.Plugin. |
| 13 | +
|
11 | 14 | * All plugin class constructors should match the signature of mypy.plugin.Plugin
|
12 |
| - (i.e. should accept an mypy.options.Options object), and *must* call super().__init__ |
13 |
| -* At several steps during semantic analysis and type checking mypy calls special `get_xxx` |
14 |
| - methods on user plugins with a single string argument that is a full name of a relevant |
15 |
| - node (see mypy.plugin.Plugin method docstrings for details) |
16 |
| -* The plugins are called in the order they are passed in the config option. Every plugin must |
17 |
| - decide whether to act on a given full name. The first plugin that returns non-None object |
18 |
| - will be used |
| 15 | + (i.e. should accept an mypy.options.Options object), and *must* call |
| 16 | + super().__init__(). |
| 17 | +
|
| 18 | +* At several steps during semantic analysis and type checking mypy calls |
| 19 | + special `get_xxx` methods on user plugins with a single string argument that |
| 20 | + is a fully qualified name (full name) of a relevant definition |
| 21 | + (see mypy.plugin.Plugin method docstrings for details). |
| 22 | +
|
| 23 | +* The plugins are called in the order they are passed in the config option. |
| 24 | + Every plugin must decide whether to act on a given full name. The first |
| 25 | + plugin that returns non-None object will be used. |
| 26 | +
|
19 | 27 | * The above decision should be made using the limited common API specified by
|
20 |
| - mypy.plugin.CommonPluginApi |
21 |
| -* The callback returned by the plugin will be called with a larger context that includes |
22 |
| - relevant current state (e.g. a default return type, or a default attribute type) and |
23 |
| - a wider relevant API provider (e.g. SemanticAnalyzerPluginInterface or |
24 |
| - CheckerPluginInterface) |
25 |
| -* The result of this is used for further processing. See various `XxxContext` named tuples |
26 |
| - for details about which information is given to each hook. |
| 28 | + mypy.plugin.CommonPluginApi. |
| 29 | +
|
| 30 | +* The callback returned by the plugin will be called with a larger context that |
| 31 | + includes relevant current state (e.g. a default return type, or a default |
| 32 | + attribute type) and a wider relevant API provider (e.g. |
| 33 | + SemanticAnalyzerPluginInterface or CheckerPluginInterface). |
| 34 | +
|
| 35 | +* The result of this is used for further processing. See various `XxxContext` |
| 36 | + named tuples for details about which information is given to each hook. |
27 | 37 |
|
28 | 38 | Plugin developers should ensure that their plugins work well in incremental and
|
29 |
| -daemon modes. In particular, plugins should not hold global state, and should always call |
30 |
| -add_plugin_dependency() in plugin hooks called during semantic analysis, see the method |
31 |
| -docstring for more details. |
32 |
| -
|
33 |
| -There is no dedicated cache storage for plugins, but plugins can store per-TypeInfo data |
34 |
| -in a special .metadata attribute that is serialized to cache between incremental runs. |
35 |
| -To avoid collisions between plugins they are encouraged to store their state |
36 |
| -under a dedicated key coinciding with plugin name in the metadata dictionary. |
37 |
| -Every value stored there must be JSON-serializable. |
| 39 | +daemon modes, and with both the old and new semantic analyzers (the old semantic |
| 40 | +analyzer will be removed soon). In particular, plugins should not hold global |
| 41 | +state, and should always call add_plugin_dependency() in plugin hooks called |
| 42 | +during semantic analysis. See the method docstring for more details. |
| 43 | +
|
| 44 | +There is no dedicated cache storage for plugins, but plugins can store |
| 45 | +per-TypeInfo data in a special .metadata attribute that is serialized to the |
| 46 | +mypy caches between incremental runs. To avoid collisions between plugins, they |
| 47 | +are encouraged to store their state under a dedicated key coinciding with |
| 48 | +plugin name in the metadata dictionary. Every value stored there must be |
| 49 | +JSON-serializable. |
| 50 | +
|
| 51 | +## New semantic analyzer |
| 52 | +
|
| 53 | +The new semantic analyzer (enabled through the --new-semantic-analyzer flag) |
| 54 | +changes how plugins are expected to work in several notable ways: |
| 55 | +
|
| 56 | +1. The order of processing AST nodes in modules is different. The old semantic |
| 57 | + analyzer processes modules in textual order, one module at a time. The new |
| 58 | + semantic analyzer first processes the module top levels, including bodies of |
| 59 | + any top-level classes and classes nested within classes. ("Top-level" here |
| 60 | + means "not nested within a function/method".) Functions and methods are |
| 61 | + processed only after module top levels have been finished. If there is an |
| 62 | + import cycle, all module top levels in the cycle are processed before |
| 63 | + processing any functions or methods. Each unit of processing (a module top |
| 64 | + level or a function/method) is called a *target*. |
| 65 | +
|
| 66 | + This also means that function signatures in the same module have not been |
| 67 | + analyzed yet when analyzing the module top level. If you need access to |
| 68 | + a function signature, you'll need to explicitly analyze the signature first |
| 69 | + using `anal_type()`. |
| 70 | +
|
| 71 | +2. Each target can be processed multiple times. This may happen if some forward |
| 72 | + references are not ready yet, for example. This means that semantic analyzer |
| 73 | + related plugin hooks can be called multiple times for the same full name. |
| 74 | + These plugin methods must thus be idempotent. |
| 75 | +
|
| 76 | +3. The `anal_type` API function returns None if some part of the type is not |
| 77 | + available yet. If this happens, the current target being analyzed will be |
| 78 | + *deferred*, which means that it will be processed again soon, in the hope |
| 79 | + that additional dependencies will be available. This may happen if there are |
| 80 | + forward references to types or inter-module references to types within an |
| 81 | + import cycle. |
| 82 | +
|
| 83 | + Note that if there is a circular definition, mypy may decide to stop |
| 84 | + processing to avoid an infinite number of iterations. When this happens, |
| 85 | + `anal_type` will generate an error and return an `AnyType` type object |
| 86 | + during the final iteration (instead of None). |
| 87 | +
|
| 88 | +4. There is a new API method `defer()`. This can be used to explicitly request |
| 89 | + the current target to be reprocessed one more time. You don't need this |
| 90 | + to call this if `anal_type` returns None, however. |
| 91 | +
|
| 92 | +5. There is a new API property `final_iteration`, which is true once mypy |
| 93 | + detected no progress during the previous iteration or if the maximum |
| 94 | + semantic analysis iteration count has been reached. You must never |
| 95 | + defer during the final iteration, as it will cause a crash. |
| 96 | +
|
| 97 | +6. The `node` attribute of SymbolTableNode objects may contain a reference to |
| 98 | + a PlaceholderNode object. This object means that this definition has not |
| 99 | + been fully processed yet. If you encounter a PlaceholderNode, you should |
| 100 | + defer unless it's the final iteration. If it's the final iteration, you |
| 101 | + should generate an error message. It usually means that there's a cyclic |
| 102 | + definition that cannot be resolved by mypy. PlaceholderNodes can only refer |
| 103 | + to references inside an import cycle. If you are looking up things from |
| 104 | + another module, such as the builtins, that is outside the current module or |
| 105 | + import cycle, you can safely assume that you won't receive a placeholder. |
| 106 | +
|
| 107 | +When testing your plugin with the new semantic analyzer, you should have a test |
| 108 | +case that forces a module top level to be processed multiple times. The easiest |
| 109 | +way to do this is to include a forward reference to a class in a top-level |
| 110 | +annotation. Example: |
| 111 | +
|
| 112 | + c: C # Forward reference causes second analysis pass |
| 113 | + class C: pass |
| 114 | +
|
| 115 | +Note that a forward reference in a function signature won't trigger another |
| 116 | +pass, since all functions are processed only after the top level has been fully |
| 117 | +analyzed. |
| 118 | +
|
| 119 | +You can use `api.options.new_semantic_analyzer` to check whether the new |
| 120 | +semantic analyzer is enabled. |
38 | 121 | """
|
39 | 122 |
|
40 | 123 | import types
|
|
0 commit comments