From d6770af5f3af07c4c89e17e674a244fd82e5d1a8 Mon Sep 17 00:00:00 2001 From: davfsa Date: Mon, 13 Feb 2023 13:24:37 +0100 Subject: [PATCH 1/9] Fix mishandling of typing.Self in attrs generated inits --- mypy/plugins/attrs.py | 17 ++++++++++++++++- test-data/unit/check-attr.test | 16 +++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 50d2955d2584..0cf8b045d72e 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -6,6 +6,7 @@ from typing_extensions import Final, Literal import mypy.plugin # To avoid circular imports. +from mypy.expandtype import expand_type from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.nodes import ( ARG_NAMED, @@ -46,6 +47,7 @@ deserialize_and_fixup_type, ) from mypy.server.trigger import make_wildcard_trigger +from mypy.state import state from mypy.typeops import make_simplified_union, map_type_from_supertype from mypy.types import ( AnyType, @@ -108,11 +110,22 @@ def __init__( self.context = context self.init_type = init_type + def expand_type(self, ctx, init_type: Type | None) -> Type | None: + if init_type is not None and self.info.self_type is not None: + # In general, it is not safe to call `expand_type()` during semantic analyzis, + # however this plugin is called very late, so all types should be fully ready. + # Also, it is tricky to avoid eager expansion of Self types here (e.g. because + # we serialize attributes). + with state.strict_optional_set(ctx.api.options.strict_optional): + return expand_type(init_type, {self.info.self_type.id: fill_typevars(self.info)}) + + return init_type + def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument: """Return this attribute as an argument to __init__.""" assert self.init - init_type: Type | None = None + init_type: Type | None if self.converter: if self.converter.init_type: init_type = self.converter.init_type @@ -147,6 +160,8 @@ def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument: else: arg_kind = ARG_OPT if self.has_default else ARG_POS + init_type = self.expand_type(ctx, init_type) + # Attrs removes leading underscores when creating the __init__ arguments. return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind) diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index f555f2ea7011..a45def168f60 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -1866,4 +1866,18 @@ reveal_type(D) # N: Revealed type is "def (a: builtins.int, b: builtins.str) -> D(1, "").a = 2 # E: Cannot assign to final attribute "a" D(1, "").b = "2" # E: Cannot assign to final attribute "b" -[builtins fixtures/property.pyi] \ No newline at end of file +[builtins fixtures/property.pyi] + +[case testSelfInClassInit] +from attrs import define +from typing import Union, Self + +@define +class C: + a: Union[Self, None] = None + +reveal_type(C) # N: Revealed type is "def (a: Union[__main__.C, None] =) -> __main__.C" +C(C()) +C(None) + +[builtins fixtures/property.pyi] From 3cc52907e9a644b0496f3c8c8132404d978ff656 Mon Sep 17 00:00:00 2001 From: davfsa Date: Mon, 13 Feb 2023 13:30:04 +0100 Subject: [PATCH 2/9] Add missing typehint --- mypy/plugins/attrs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 0cf8b045d72e..933a4f6bf159 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -110,7 +110,7 @@ def __init__( self.context = context self.init_type = init_type - def expand_type(self, ctx, init_type: Type | None) -> Type | None: + def expand_type(self, ctx: mypy.plugin.ClassDefContext, init_type: Type | None) -> Type | None: if init_type is not None and self.info.self_type is not None: # In general, it is not safe to call `expand_type()` during semantic analyzis, # however this plugin is called very late, so all types should be fully ready. From d76e440e1586ebb08e66baae83a4584dfa6ba774 Mon Sep 17 00:00:00 2001 From: davfsa Date: Tue, 14 Feb 2023 01:02:13 +0100 Subject: [PATCH 3/9] Add missing flag to test --- test-data/unit/check-attr.test | 1 + 1 file changed, 1 insertion(+) diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index a45def168f60..68b4bed8ce7f 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -1869,6 +1869,7 @@ D(1, "").b = "2" # E: Cannot assign to final attribute "b" [builtins fixtures/property.pyi] [case testSelfInClassInit] +# flags: --strict-optional from attrs import define from typing import Union, Self From 6d0615d8d465a10b95c52bcff39dc30cf90ada66 Mon Sep 17 00:00:00 2001 From: davfsa Date: Tue, 14 Feb 2023 01:02:24 +0100 Subject: [PATCH 4/9] De-duplicate code --- mypy/checker.py | 8 +++++--- mypy/checkmember.py | 6 +++--- mypy/expandtype.py | 18 ++++++++++++------ mypy/plugins/attrs.py | 16 +++------------- mypy/plugins/dataclasses.py | 16 +++------------- mypy/subtypes.py | 2 +- 6 files changed, 27 insertions(+), 39 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 8e1de9a07b4c..41812f8a48fb 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2492,9 +2492,9 @@ class C(B, A[int]): ... # this is unsafe because... ok = is_subtype(first_sig, second_sig, ignore_pos_arg_names=True) elif first_type and second_type: if isinstance(first.node, Var): - first_type = expand_self_type(first.node, first_type, fill_typevars(ctx)) + first_type = expand_self_type(first_type, first.node.info, fill_typevars(ctx)) if isinstance(second.node, Var): - second_type = expand_self_type(second.node, second_type, fill_typevars(ctx)) + second_type = expand_self_type(second_type, second.node.info, fill_typevars(ctx)) ok = is_equivalent(first_type, second_type) if not ok: second_node = base2[name].node @@ -3062,7 +3062,9 @@ def lvalue_type_from_base( base_node = base_var.node base_type = base_var.type if isinstance(base_node, Var) and base_type is not None: - base_type = expand_self_type(base_node, base_type, fill_typevars(expr_node.info)) + base_type = expand_self_type( + base_type, base_node.info, fill_typevars(expr_node.info) + ) if isinstance(base_node, Decorator): base_node = base_node.func base_type = base_node.type diff --git a/mypy/checkmember.py b/mypy/checkmember.py index a2c580e13446..30e4ba2a5e86 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -737,7 +737,7 @@ def analyze_var( if not (mx.is_self or mx.is_super) or supported_self_type( get_proper_type(mx.original_type) ): - t = expand_self_type(var, t, mx.original_type) + t = expand_self_type(t, var.info, mx.original_type) t = get_proper_type(expand_type_by_instance(t, itype)) freeze_all_type_vars(t) result: Type = t @@ -768,7 +768,7 @@ def analyze_var( # and similarly for B1 when checking against B dispatched_type = meet.meet_types(mx.original_type, itype) signature = freshen_all_functions_type_vars(functype) - bound = get_proper_type(expand_self_type(var, signature, mx.original_type)) + bound = get_proper_type(expand_self_type(signature, var.info, mx.original_type)) assert isinstance(bound, FunctionLike) signature = bound signature = check_self_arg( @@ -986,7 +986,7 @@ def analyze_class_attribute_access( # In the above example this means that we infer following types: # C.x -> Any # C[int].x -> int - t = get_proper_type(expand_self_type(node.node, t, itype)) + t = get_proper_type(expand_self_type(t, node.node.info, itype)) t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars}) is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or ( diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 7933283b24d6..a17dc76206de 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -3,7 +3,7 @@ from typing import Iterable, Mapping, Sequence, TypeVar, cast, overload from typing_extensions import Final -from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, Var +from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, TypeInfo from mypy.type_visitor import TypeTranslator from mypy.types import ( ANY_STRATEGY, @@ -508,17 +508,23 @@ def expand_unpack_with_variables( @overload -def expand_self_type(var: Var, typ: ProperType, replacement: ProperType) -> ProperType: +def expand_self_type(typ: ProperType, info: TypeInfo, replacement: ProperType) -> ProperType: ... @overload -def expand_self_type(var: Var, typ: Type, replacement: Type) -> Type: +def expand_self_type(typ: Type, info: TypeInfo, replacement: Type) -> Type: ... -def expand_self_type(var: Var, typ: Type, replacement: Type) -> Type: +@overload +def expand_self_type(typ: None, info: TypeInfo, replacement: Type | None) -> None: + ... + + +def expand_self_type(typ: Type | None, info: TypeInfo, replacement: Type | None) -> Type | None: """Expand appearances of Self type in a variable type.""" - if var.info.self_type is not None and not var.is_property: - return expand_type(typ, {var.info.self_type.id: replacement}) + if typ is not None and info.self_type is not None: + return expand_type(typ, {info.self_type.id: replacement}) + return typ diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 933a4f6bf159..74bbdcdfb93b 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -6,7 +6,7 @@ from typing_extensions import Final, Literal import mypy.plugin # To avoid circular imports. -from mypy.expandtype import expand_type +from mypy.expandtype import expand_self_type from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.nodes import ( ARG_NAMED, @@ -110,17 +110,6 @@ def __init__( self.context = context self.init_type = init_type - def expand_type(self, ctx: mypy.plugin.ClassDefContext, init_type: Type | None) -> Type | None: - if init_type is not None and self.info.self_type is not None: - # In general, it is not safe to call `expand_type()` during semantic analyzis, - # however this plugin is called very late, so all types should be fully ready. - # Also, it is tricky to avoid eager expansion of Self types here (e.g. because - # we serialize attributes). - with state.strict_optional_set(ctx.api.options.strict_optional): - return expand_type(init_type, {self.info.self_type.id: fill_typevars(self.info)}) - - return init_type - def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument: """Return this attribute as an argument to __init__.""" assert self.init @@ -160,7 +149,8 @@ def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument: else: arg_kind = ARG_OPT if self.has_default else ARG_POS - init_type = self.expand_type(ctx, init_type) + with state.strict_optional_set(ctx.api.options.strict_optional): + init_type = expand_self_type(init_type, self.info, fill_typevars(self.info)) # Attrs removes leading underscores when creating the __init__ arguments. return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 3feb644dc8ea..fb8d354405f6 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -2,10 +2,9 @@ from __future__ import annotations -from typing import Optional from typing_extensions import Final -from mypy.expandtype import expand_type +from mypy.expandtype import expand_self_type from mypy.nodes import ( ARG_NAMED, ARG_NAMED_OPT, @@ -106,22 +105,13 @@ def to_argument(self, current_info: TypeInfo) -> Argument: arg_kind = ARG_OPT return Argument( variable=self.to_var(current_info), - type_annotation=self.expand_type(current_info), + type_annotation=expand_self_type(self.type, self.info, fill_typevars(current_info)), initializer=None, kind=arg_kind, ) - def expand_type(self, current_info: TypeInfo) -> Optional[Type]: - if self.type is not None and self.info.self_type is not None: - # In general, it is not safe to call `expand_type()` during semantic analyzis, - # however this plugin is called very late, so all types should be fully ready. - # Also, it is tricky to avoid eager expansion of Self types here (e.g. because - # we serialize attributes). - return expand_type(self.type, {self.info.self_type.id: fill_typevars(current_info)}) - return self.type - def to_var(self, current_info: TypeInfo) -> Var: - return Var(self.name, self.expand_type(current_info)) + return Var(self.name, expand_self_type(self.type, self.info, fill_typevars(current_info))) def serialize(self) -> JsonDict: assert self.type diff --git a/mypy/subtypes.py b/mypy/subtypes.py index c3d5517d43dd..2706c62082c6 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1249,7 +1249,7 @@ def find_node_type( else: typ = node.type if typ is not None: - typ = expand_self_type(node, typ, subtype) + typ = expand_self_type(typ, node.info, subtype) p_typ = get_proper_type(typ) if typ is None: return AnyType(TypeOfAny.from_error) From 95488d8f0729fc50170d72cd1356b5afbe97bb63 Mon Sep 17 00:00:00 2001 From: davfsa Date: Tue, 14 Feb 2023 01:14:08 +0100 Subject: [PATCH 5/9] Fix typehint --- mypy/expandtype.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index a17dc76206de..bee83e1cd127 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -518,11 +518,11 @@ def expand_self_type(typ: Type, info: TypeInfo, replacement: Type) -> Type: @overload -def expand_self_type(typ: None, info: TypeInfo, replacement: Type | None) -> None: +def expand_self_type(typ: None, info: TypeInfo, replacement: Type) -> None: ... -def expand_self_type(typ: Type | None, info: TypeInfo, replacement: Type | None) -> Type | None: +def expand_self_type(typ: Type | None, info: TypeInfo, replacement: Type) -> Type | None: """Expand appearances of Self type in a variable type.""" if typ is not None and info.self_type is not None: return expand_type(typ, {info.self_type.id: replacement}) From 23b9ab5916a411c7f926655458c6147f3eada7c3 Mon Sep 17 00:00:00 2001 From: davfsa Date: Wed, 15 Feb 2023 18:51:41 +0100 Subject: [PATCH 6/9] Add recommended test and fix small bug --- mypy/plugins/attrs.py | 2 +- test-data/unit/check-attr.test | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 8b9adc44b64d..63e44b6f4792 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -151,7 +151,7 @@ def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument: arg_kind = ARG_OPT if self.has_default else ARG_POS with state.strict_optional_set(ctx.api.options.strict_optional): - init_type = expand_self_type(init_type, self.info, fill_typevars(self.info)) + init_type = expand_self_type(init_type, self.info, fill_typevars(ctx.cls.info)) # Attrs removes leading underscores when creating the __init__ arguments. return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind) diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index f1862f098ed9..898fbfa02583 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -1880,5 +1880,15 @@ class C: reveal_type(C) # N: Revealed type is "def (a: Union[__main__.C, None] =) -> __main__.C" C(C()) C(None) +reveal_type(C(C()).a) # N: Revealed type is "Union[__main__.C, None]" + +@define +class S(C): + ... + +reveal_type(S) # N: Revealed type is "def (a: Union[__main__.S, None] =) -> __main__.S" +S(S()) +S(None) +reveal_type(S(S()).a) # N: Revealed type is "Union[__main__.S, None]" [builtins fixtures/property.pyi] From 1dba00ada33ca5c355e72d90e9d2cd1876fe04bd Mon Sep 17 00:00:00 2001 From: davfsa Date: Fri, 17 Feb 2023 10:19:42 +0100 Subject: [PATCH 7/9] Revert merging of functions --- mypy/checker.py | 8 +++----- mypy/checkmember.py | 6 +++--- mypy/expandtype.py | 18 ++++++------------ mypy/plugins/attrs.py | 13 +++++++++++-- mypy/plugins/dataclasses.py | 18 ++++++++++++------ mypy/subtypes.py | 2 +- 6 files changed, 36 insertions(+), 29 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 41812f8a48fb..8e1de9a07b4c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2492,9 +2492,9 @@ class C(B, A[int]): ... # this is unsafe because... ok = is_subtype(first_sig, second_sig, ignore_pos_arg_names=True) elif first_type and second_type: if isinstance(first.node, Var): - first_type = expand_self_type(first_type, first.node.info, fill_typevars(ctx)) + first_type = expand_self_type(first.node, first_type, fill_typevars(ctx)) if isinstance(second.node, Var): - second_type = expand_self_type(second_type, second.node.info, fill_typevars(ctx)) + second_type = expand_self_type(second.node, second_type, fill_typevars(ctx)) ok = is_equivalent(first_type, second_type) if not ok: second_node = base2[name].node @@ -3062,9 +3062,7 @@ def lvalue_type_from_base( base_node = base_var.node base_type = base_var.type if isinstance(base_node, Var) and base_type is not None: - base_type = expand_self_type( - base_type, base_node.info, fill_typevars(expr_node.info) - ) + base_type = expand_self_type(base_node, base_type, fill_typevars(expr_node.info)) if isinstance(base_node, Decorator): base_node = base_node.func base_type = base_node.type diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 30e4ba2a5e86..a2c580e13446 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -737,7 +737,7 @@ def analyze_var( if not (mx.is_self or mx.is_super) or supported_self_type( get_proper_type(mx.original_type) ): - t = expand_self_type(t, var.info, mx.original_type) + t = expand_self_type(var, t, mx.original_type) t = get_proper_type(expand_type_by_instance(t, itype)) freeze_all_type_vars(t) result: Type = t @@ -768,7 +768,7 @@ def analyze_var( # and similarly for B1 when checking against B dispatched_type = meet.meet_types(mx.original_type, itype) signature = freshen_all_functions_type_vars(functype) - bound = get_proper_type(expand_self_type(signature, var.info, mx.original_type)) + bound = get_proper_type(expand_self_type(var, signature, mx.original_type)) assert isinstance(bound, FunctionLike) signature = bound signature = check_self_arg( @@ -986,7 +986,7 @@ def analyze_class_attribute_access( # In the above example this means that we infer following types: # C.x -> Any # C[int].x -> int - t = get_proper_type(expand_self_type(t, node.node.info, itype)) + t = get_proper_type(expand_self_type(node.node, t, itype)) t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars}) is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or ( diff --git a/mypy/expandtype.py b/mypy/expandtype.py index bee83e1cd127..f227a2676587 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -3,7 +3,7 @@ from typing import Iterable, Mapping, Sequence, TypeVar, cast, overload from typing_extensions import Final -from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, TypeInfo +from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, TypeInfo, Var from mypy.type_visitor import TypeTranslator from mypy.types import ( ANY_STRATEGY, @@ -508,23 +508,17 @@ def expand_unpack_with_variables( @overload -def expand_self_type(typ: ProperType, info: TypeInfo, replacement: ProperType) -> ProperType: +def expand_self_type(var: Var, typ: ProperType, replacement: ProperType) -> ProperType: ... @overload -def expand_self_type(typ: Type, info: TypeInfo, replacement: Type) -> Type: +def expand_self_type(var: Var, typ: Type, replacement: Type) -> Type: ... -@overload -def expand_self_type(typ: None, info: TypeInfo, replacement: Type) -> None: - ... - - -def expand_self_type(typ: Type | None, info: TypeInfo, replacement: Type) -> Type | None: +def expand_self_type(var: Var, typ: Type, replacement: Type) -> Type: """Expand appearances of Self type in a variable type.""" - if typ is not None and info.self_type is not None: - return expand_type(typ, {info.self_type.id: replacement}) - + if var.info.self_type is not None and not var.is_property: + return expand_type(typ, {var.info.self_type.id: replacement}) return typ diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 63e44b6f4792..5422c8ac8c22 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -7,7 +7,7 @@ import mypy.plugin # To avoid circular imports. from mypy.errorcodes import LITERAL_REQ -from mypy.expandtype import expand_self_type +from mypy.expandtype import expand_self_type, expand_type from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.nodes import ( ARG_NAMED, @@ -111,6 +111,15 @@ def __init__( self.context = context self.init_type = init_type + def expand_type(self, typ: Type, ctx: mypy.plugin.ClassDefContext) -> Type | None: + if typ is not None and self.info.self_type is not None: + # In general, it is not safe to call `expand_type()` during semantic analyzis, + # however this plugin is called very late, so all types should be fully ready. + # Also, it is tricky to avoid eager expansion of Self types here (e.g. because + # we serialize attributes). + return expand_type(typ, {self.info.self_type.id: fill_typevars(ctx.cls.info)}) + return typ + def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument: """Return this attribute as an argument to __init__.""" assert self.init @@ -151,7 +160,7 @@ def argument(self, ctx: mypy.plugin.ClassDefContext) -> Argument: arg_kind = ARG_OPT if self.has_default else ARG_POS with state.strict_optional_set(ctx.api.options.strict_optional): - init_type = expand_self_type(init_type, self.info, fill_typevars(ctx.cls.info)) + init_type = self.expand_type(init_type, ctx) # Attrs removes leading underscores when creating the __init__ arguments. return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 983bbffb0cc2..958b474e1552 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -5,7 +5,7 @@ from typing_extensions import Final from mypy import errorcodes, message_registry -from mypy.expandtype import expand_self_type +from mypy.expandtype import expand_self_type, expand_type from mypy.nodes import ( ARG_NAMED, ARG_NAMED_OPT, @@ -108,16 +108,22 @@ def to_argument(self, current_info: TypeInfo) -> Argument: arg_kind = ARG_OPT return Argument( variable=self.to_var(current_info), - type_annotation=expand_self_type(self.type, self.info, fill_typevars(current_info)), + type_annotation=self.expand_type(current_info), initializer=None, kind=arg_kind, ) + def expand_type(self, current_info: TypeInfo) -> Type | None: + if self.type is not None and self.info.self_type is not None: + # In general, it is not safe to call `expand_type()` during semantic analyzis, + # however this plugin is called very late, so all types should be fully ready. + # Also, it is tricky to avoid eager expansion of Self types here (e.g. because + # we serialize attributes). + return expand_type(self.type, {self.info.self_type.id: fill_typevars(current_info)}) + return self.type + def to_var(self, current_info: TypeInfo) -> Var: - return Var( - self.alias or self.name, - expand_self_type(self.type, self.info, fill_typevars(current_info)), - ) + return Var(self.alias or self.name, self.expand_type(current_info)) def serialize(self) -> JsonDict: assert self.type diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 2706c62082c6..c3d5517d43dd 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1249,7 +1249,7 @@ def find_node_type( else: typ = node.type if typ is not None: - typ = expand_self_type(typ, node.info, subtype) + typ = expand_self_type(node, typ, subtype) p_typ = get_proper_type(typ) if typ is None: return AnyType(TypeOfAny.from_error) From 96c564706b2c3df88542a5f9566ed36ab48d2f1e Mon Sep 17 00:00:00 2001 From: davfsa Date: Fri, 17 Feb 2023 10:22:10 +0100 Subject: [PATCH 8/9] Remove un-used imports --- mypy/expandtype.py | 2 +- mypy/plugins/attrs.py | 2 +- mypy/plugins/dataclasses.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index f227a2676587..7933283b24d6 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -3,7 +3,7 @@ from typing import Iterable, Mapping, Sequence, TypeVar, cast, overload from typing_extensions import Final -from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, TypeInfo, Var +from mypy.nodes import ARG_POS, ARG_STAR, ArgKind, Var from mypy.type_visitor import TypeTranslator from mypy.types import ( ANY_STRATEGY, diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 5422c8ac8c22..8a7d9a6d0e62 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -7,7 +7,7 @@ import mypy.plugin # To avoid circular imports. from mypy.errorcodes import LITERAL_REQ -from mypy.expandtype import expand_self_type, expand_type +from mypy.expandtype import expand_type from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type from mypy.nodes import ( ARG_NAMED, diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 958b474e1552..d9cdb5d46f86 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -5,7 +5,7 @@ from typing_extensions import Final from mypy import errorcodes, message_registry -from mypy.expandtype import expand_self_type, expand_type +from mypy.expandtype import expand_type from mypy.nodes import ( ARG_NAMED, ARG_NAMED_OPT, From 930e0663e92422faadd6016f6ff58c4ef0efb214 Mon Sep 17 00:00:00 2001 From: davfsa Date: Fri, 17 Feb 2023 10:32:16 +0100 Subject: [PATCH 9/9] Fix typehint --- mypy/plugins/attrs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 8a7d9a6d0e62..3bfe61f8753f 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -111,7 +111,7 @@ def __init__( self.context = context self.init_type = init_type - def expand_type(self, typ: Type, ctx: mypy.plugin.ClassDefContext) -> Type | None: + def expand_type(self, typ: Type | None, ctx: mypy.plugin.ClassDefContext) -> Type | None: if typ is not None and self.info.self_type is not None: # In general, it is not safe to call `expand_type()` during semantic analyzis, # however this plugin is called very late, so all types should be fully ready.