You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The mypy plugin interface is experimental, unstable, and prone to change. In particular, there are no guarantees about backwards compatibility. Backwards incompatible changes may be made without a deprecation period.
We will, however, attempt to announce breaking changes in this issue, so that plugin developers can subscribe to this issue and be notified.
Breaking changes fall into three broad categories:
Changes to the actual plugin API itself
Changes to parts of the "implicit plugin API"---that is, internals that plugins are likely to use (representation of types, etc).
Changes to things that really aren't plausibly part of the plugin API (but, of course, that some plugins might be using anyway...)
Issues in category 1 will be consistently announced here, issues in category 3 will probably be announced here only if problems are reported, and issues in category 2 will be somewhere in the middle.
And we'll start this off with a belated category 3 announcement:
The default wheels for mypy 0.700 are compiled with mypyc. This breaks monkey-patching of mypy internals.
If you are the author of a mypy plugin that relies on monkey-patching mypy internals, get in touch with us and we can probably find a better approach. (For example, #6598 added hooks needed by the django plugin.)
The new semantic analyzer requires changes to some plugins, especially those that modify classes. In particular, hooks may be executed multiple times for the same definitions. PR #7135 added documentation about how to support the new semantic analyzer.
Note that mypy 0.720 (to be released soon) will enable the semantic analyzer by default, and the next release after that will remove the old semantic analyzer.
To test that a plugin works with the semantic analyzer, you should have test cases that cause mypy to analyze things twice. The easiest way to achieve is to add a forward reference to a type at module top level:
forwardref: C# Forward reference to C causes deferralclassC: pass# ... followed by whatever you want to test
PR #7829 makes all name and fullname methods in mypy into properties. This will unfortunately require changes to many plugins. We've decided that it is worth removing a long-standing pain point and that it is better to do it sooner than later.
sed can be used to update code to the new version with something like sed -i -e 's/\.name()/.name/g' -e 's/\.fullname()/.fullname/g'
If your plugin wishes to support older and newer versions during a transition period, this can be done with these helper functions:
from typing import Union
from mypy.nodes import FuncBase, SymbolNode
def fullname(x: Union[FuncBase, SymbolNode]) -> str:
fn = x.fullname
if callable(fn):
return fn()
return fn
def name(x: Union[FuncBase, SymbolNode]) -> str:
fn = x.name
if callable(fn):
return fn()
return fn
I don't have an automated way to convert code to use these, but if somebody produces one and sends it to me I will update this post.
PR #15369 adds get_expression_type to the CheckerPluginInterface. This enables the common scenario in method/function signature hooks, where the actual type of an argument affects the rest of the signature.
PR #14872 (v1.4.0) adds a new required argument - default - to all TypeVarLikeExpr and TypeVarLikeType types. This is in preparation for PEP 696 (TypeVar defaults) support.
If a plugin constructs these expression / types manually, a version guard needs to be added. E.g.
PR #17644, included in mypy 1.12, changed the signature of TypedDictType.__init__ (link). It now has an extra readonly_keys: set[str] parameter.
I'm seeing a crash from that:
mypy --check-untyped-defs --show-traceback .
./rest_api/tests/test_api.py:134: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 1.11.2
Traceback (most recent call last):
File "mypy/checkexpr.py", line 5830, in accept
File "mypy/nodes.py", line 1969, in accept
File "mypy/checkexpr.py", line 480, in visit_call_expr
File "mypy/checkexpr.py", line 614, in visit_call_expr_inner
File "mypy/checkexpr.py", line 1467, in check_call_expr_with_callee_type
File "mypy/checkexpr.py", line 1561, in check_call
File "mypy/checkexpr.py", line 1803, in check_callable_call
File "mypy/checkexpr.py", line 1258, in apply_function_plugin
File ".../my-venv/lib/python3.12/site-packages/mypy_django_plugin/transformers/querysets.py", line 357, in extract_proper_type_queryset_values
row_type = helpers.make_typeddict(ctx.api, column_types, set(column_types.keys()), set())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../my-venv/lib/python3.12/site-packages/mypy_django_plugin/lib/helpers.py", line 408, in make_typeddict
typed_dict_type = TypedDictType(
^^^^^^^^^^^^^^
TypeError: 'readonly_keys' is an invalid keyword argument for__init__()
./rest_api/tests/test_api.py:134: : note: use --pdb to drop into pdb
You need to use a newer version of mypy or older version of django-stubs (django-stubs should maybe have branched this code on mypy version or set a mypy lower bound in their dependencies). This isn't the right issue to get help; I'll lock posting on this issue to maintainers so that plugin authors can follow this issue without noise.
I would be surprised if this broke anything, but the descendants of https://github.com/python/mypy/pull/18356 will make a few args in the plugin API positional-only (in cases that there were already mismatches in mypy's codebase)
Since recently, code in checkmember.py is used in more contexts, in particular in method/attribute override checks (starting v1.16), and protocol subtyping checks (starting v1.17). This means that the method/attribute plugin hooks may be called in more contexts. (Also note that some related internal functions were moved and/or changed signatures).
Note that #19627 turns TypeCheck.local_type_map from method into attribute (for performance reasons). This is a private attribute, but I guess some people may use it, so letting everyone know here just in case.
In mypy 1.18, the type of CallableType.variables was changed from a list to a tuple (#19768). This may impact plugins that assign to attributes. You'll need to assign a tuple or there will be a runtime type error.
IMPORTANT: We don't recommend that plugins assign to attributes of type objects, since type objects are effectively considered value objects and should ideally be immutable. This is sometimes hard/impossible to avoid -- if you have a use case that requires assigning to an attribute, feel free to open an issue to discuss alternatives. In the future we may remove the setters from at least some attributes from type objects. It's better to use copy_modified methods or construct new instances instead of modifying existing instances. (Technically the type objects are not part of any stable API, but we'll try to avoid making arbitrary changes to them unless there is a good reason to do so.)
In mypy v2.0 SymbolTableNode.node will became a read-only property. One should normally never mutate it, but if strictly necessary you can instead mutate private SymbolTableNode._node in mypy v2.0.
Activity
msullivan commentedon Apr 3, 2019
And we'll start this off with a belated category 3 announcement:
The default wheels for mypy 0.700 are compiled with mypyc. This breaks monkey-patching of mypy internals.
If you are the author of a mypy plugin that relies on monkey-patching mypy internals, get in touch with us and we can probably find a better approach. (For example, #6598 added hooks needed by the django plugin.)
Add some more plugin docs
Add some more plugin docs (#6618)
gvanrossum commentedon Apr 24, 2019
(Sorry for the accidental unpin. Fixed now.)
JukkaL commentedon Jul 11, 2019
The new semantic analyzer requires changes to some plugins, especially those that modify classes. In particular, hooks may be executed multiple times for the same definitions. PR #7135 added documentation about how to support the new semantic analyzer.
Note that mypy 0.720 (to be released soon) will enable the semantic analyzer by default, and the next release after that will remove the old semantic analyzer.
PRs #7136, #7132, #7096, #6987, #6984, #6724 and #6515 contain examples of changes that may be needed to plugins.
To test that a plugin works with the semantic analyzer, you should have test cases that cause mypy to analyze things twice. The easiest way to achieve is to add a forward reference to a type at module top level:
msullivan commentedon Aug 27, 2019
PR #7397 moves around some functions as part of untangling the cyclic imports in mypy.
The most prominent change and the most likely to impact plugins is:
mypy.types.UnionType.make_simplified_union->mypy.typeops.make_simplified_unionAdditionally:
mypy.types.TypeVarDef.erase_to_union_or_bound->mypy.types.typeops.erase_def_to_union_or_boundmypy.types.TypeVarType.erase_to_union_or_bound->mypy.types.typeops.erase_to_union_or_boundmypy.types.{true_only, false_only, true_or_false}->mypy.typeopsmypy.types.CallableType.corresponding_argument->mypy.types.typeops.corresponding_argumentmypy.checkmemberhave been moved tomypy.typeops. The most prominent here isbind_self.msullivan commentedon Nov 13, 2019
PR #7829 makes all
nameandfullnamemethods in mypy into properties. This will unfortunately require changes to many plugins. We've decided that it is worth removing a long-standing pain point and that it is better to do it sooner than later.sed can be used to update code to the new version with something like
sed -i -e 's/\.name()/.name/g' -e 's/\.fullname()/.fullname/g'If your plugin wishes to support older and newer versions during a transition period, this can be done with these helper functions:
I don't have an automated way to convert code to use these, but if somebody produces one and sends it to me I will update this post.
Sorry for the inconvenience!
15 remaining items
ikonst commentedon Jun 11, 2023
PR #15369 adds
get_expression_typeto theCheckerPluginInterface. This enables the common scenario in method/function signature hooks, where the actual type of an argument affects the rest of the signature.This change is backwards compatible.
For example:
cdce8p commentedon Jun 21, 2023
PR #14872 (
v1.4.0) adds a new required argument -default- to allTypeVarLikeExprandTypeVarLikeTypetypes. This is in preparation for PEP 696 (TypeVar defaults) support.If a plugin constructs these expression / types manually, a version guard needs to be added. E.g.
If no explicit default value is provided,
AnyType(TypeOfAny.from_omitted_generics)should be used.JukkaL commentedon Oct 15, 2024
PR #17644, included in mypy 1.12, changed the signature of
TypedDictType.__init__(link). It now has an extrareadonly_keys: set[str]parameter.BrentWilkins commentedon Nov 12, 2024
I'm seeing a crash from that:
My versions:
hauntsaninja commentedon Nov 12, 2024
You need to use a newer version of mypy or older version of django-stubs (django-stubs should maybe have branched this code on mypy version or set a mypy lower bound in their dependencies). This isn't the right issue to get help; I'll lock posting on this issue to maintainers so that plugin authors can follow this issue without noise.
hauntsaninja commentedon Dec 29, 2024
I would be surprised if this broke anything, but the descendants of https://github.com/python/mypy/pull/18356 will make a few args in the plugin API positional-only (in cases that there were already mismatches in mypy's codebase)
ilevkivskyi commentedon Aug 6, 2025
Since recently, code in
checkmember.pyis used in more contexts, in particular in method/attribute override checks (starting v1.16), and protocol subtyping checks (starting v1.17). This means that the method/attribute plugin hooks may be called in more contexts. (Also note that some related internal functions were moved and/or changed signatures).ilevkivskyi commentedon Aug 9, 2025
Note that #19627 turns
TypeCheck.local_type_mapfrom method into attribute (for performance reasons). This is a private attribute, but I guess some people may use it, so letting everyone know here just in case.JukkaL commentedon Sep 12, 2025
In mypy 1.18, the type of
CallableType.variableswas changed from a list to a tuple (#19768). This may impact plugins that assign to attributes. You'll need to assign a tuple or there will be a runtime type error.IMPORTANT: We don't recommend that plugins assign to attributes of type objects, since type objects are effectively considered value objects and should ideally be immutable. This is sometimes hard/impossible to avoid -- if you have a use case that requires assigning to an attribute, feel free to open an issue to discuss alternatives. In the future we may remove the setters from at least some attributes from type objects. It's better to use
copy_modifiedmethods or construct new instances instead of modifying existing instances. (Technically the type objects are not part of any stable API, but we'll try to avoid making arbitrary changes to them unless there is a good reason to do so.)hauntsaninja commentedon Nov 30, 2025
In mypy 1.19, #19798 adds a
modulerequired parameter tomypy.nodes.TypeAliasilevkivskyi commentedon Feb 22, 2026
Note that
Instance.invalidattribute will be deleted in v1.20. It was mostly unused anyway, but please let me know if you need it for some reason.ilevkivskyi commentedon Apr 9, 2026
In mypy v2.0
SymbolTableNode.nodewill became a read-only property. One should normally never mutate it, but if strictly necessary you can instead mutate privateSymbolTableNode._nodein mypy v2.0.