-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Announcement issue for plugin API changes #6617
Comments
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.) |
* Link to plugin changes announcement issue (#6617) * Document get_additional_deps()
* Link to plugin changes announcement issue (#6617) * Document get_additional_deps()
(Sorry for the accidental unpin. Fixed now.) |
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: forwardref: C # Forward reference to C causes deferral
class C: pass
# ... followed by whatever you want to test |
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:
Additionally:
|
PR #7829 makes all sed can be used to update code to the new version with something like 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! |
This PR starts using the new `TypeAliasType` in the semantic analyzer. This PR doesn't yet pulls the trigger to enable the recursive types, but it is now essentially one line of code away. This PR: * Makes type analyzer return a `TypeAliasType` instead of eagerly expanding the alias target. * Refactors `TypeAliasExpr` to refer to `TypeAlias` (sorry for the noise). * Makes few minor fixes to make all existing tests pass. * Adds few logistic changes around `get_proper_type()` I found necessary while playing with actual recursive types over the weekend. Here are some strategical comments: * Taking into account how easy it was to make all existing tests pass, I don't think it is necessary to introduce a hidden option flag that would eagerly expand all type aliases after semantic analyzis. It would probably make sense to test this well locally before a public release. * There is a special case for no arguments generic aliases. Currently one is allowed to write `L = List; x: L[int]`, I preserve this by using eager expansion in this special case, otherwise it would complicate the whole logic significantly. This is also mostly a legacy thing because we have built-in aliases like `List = list` magically added by semantic analyzer. * I have found that just carelessly sprinkling `get_proper_type()` is not a best strategy. It saves all the existing special-casing but also introduces a risk for infinite recursion. In particular, "type ops tangle" should ideally always pass on the original alias type. Unfortunately, there is no way to fix/enforce this (without having some severe performance impact). Note it is mostly fine to "carelessly" use `get_proper_type()` in the "front end" (like `checker.py`, `checkexpr.py`, `checkmember.py` etc). Here is my plan for the next five PRs: 1. I am going to try merging `SubtypeVisitor` and `ProperSubtypeVisitor`, there is very large amount of code duplication (there is already an issue for this). 2. I am going to try to get rid of `sametypes.py` (I am going to open a new issue, see my arguments there). 3. I am going to enable the recursive aliases and add sufficiently many tests to be sure we are safe about infinite recursion in type ops. 4. I am going to change how named tuples and typed dicts are represented internally, currently they are stored as `TypeInfo`s, but will be stored as `TypeAlias`. Essentially there will be almost no difference between `A = Tuple[int, int]` and `A = NamedTuple('A', [('x', int), ('y', int)])`. This will allow typed dicts and named tuple participate in recursive types. 5. I am going to switch from using unbound type variables to bound type variables for generic type aliases, since now they are almost identical to `TypeInfo`s so it IMO it really makes sense to make them uniform (and avoid confusions and code duplication in future). 5a. Potentially as a follow-up I may add support for generic named tuples and typed dicts, since steps 4 plus 5 will make this almost trivial. There is another important thing to call out, previously unions never contained another unions as items (because constructor flattened them), and some code might implicitly rely on this. IMO we should probably update these places, since maintaining this guarantee may be painful. Yet another important thing is that this may break many plugins, so we need to announce this in #6617 when will merge this.
PR #7923 changed the internal representation of type aliases in mypy. Previously, type aliases were always eagerly expanded. For example, in this case: Alias = List[int]
x: Alias the type of the There are two helper functions There is also a mypy plugin to type-check your mypy plugins, see Sorry for the inconvenience! |
(An additional small reminder related to last two comments: don't forget that a plugin entry point gets the mypy version string, you can use it for more flexibility.) |
[Category 2 change] PR #9951 gets rid of TypeVarDef; use TypeVarType instead. If you're wondering what the difference between them was, so was I, which is why there's only one of them now. |
Thanks! It affects some of my code: |
pydantic/pydantic#3528 python/mypy#6617 (comment) pydantic/pydantic#3175 (comment) updating mypy in build yml and requirements to 0.910
pydantic/pydantic#3528 python/mypy#6617 (comment) pydantic/pydantic#3175 (comment) updating mypy in build yml and requirements to 0.910 Co-authored-by: stas <[email protected]>
Deprecated We brought back the |
PR #11332 changes |
pydantic/pydantic#3528 python/mypy#6617 (comment) pydantic/pydantic#3175 (comment) updating mypy in build yml and requirements to 0.910 Co-authored-by: stas <[email protected]>
PR #14435 changes the runtime type of various (but not all!) if n.fullname is not None:
# do something with n.fullname It can be updated like this, since an empty string is falsy (this also works with older mypy versions that use if n.fullname:
# do something with n.fullname |
PR #15369 adds This change is backwards compatible. For example: first_arg = ctx.args[0][0]
first_arg_type = ctx.api.get_expression_type(first_arg)
return ctx.default_signature.copy_modified(
arg_types=[first_arg_type, first_arg_type], # 1st arg affects 2nd arg's type
) |
PR #14872 ( If a plugin constructs these expression / types manually, a version guard needs to be added. E.g. from mypy.nodes import TypeVarExpr
from mypy.types import TypeVarType, AnyType, TypeOfAny
def parse_mypy_version(version: str) -> tuple[int, ...]:
return tuple(map(int, version.partition('+')[0].split('.')))
MYPY_VERSION_TUPLE = parse_mypy_version(mypy_version)
# ...
if MYPY_VERSION_TUPLE >= (1, 4):
tvt = TypeVarType(
self_tvar_name,
tvar_fullname,
-1,
[],
obj_type,
AnyType(TypeOfAny.from_omitted_generics), # <-- new!
)
self_tvar_expr = TypeVarExpr(
self_tvar_name,
tvar_fullname,
[],
obj_type,
AnyType(TypeOfAny.from_omitted_generics), # <-- new!
)
else:
tvt = TypeVarType(self_tvar_name, tvar_fullname, -1, [], obj_type)
self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [], obj_type) If no explicit default value is provided, |
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 My versions:
|
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. |
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:
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.
The text was updated successfully, but these errors were encountered: