From 0ecd91068640f3b001659b929f3e36a59483c3a7 Mon Sep 17 00:00:00 2001 From: uko Date: Sun, 18 May 2025 12:34:50 +0900 Subject: [PATCH 01/23] Fix handling of overloaded __init__ methods in attrs plugin --- mypy/plugins/attrs.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index b7b3821576ea..3b7d3af45eb1 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1030,8 +1030,16 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None: if magic_attr is None or not magic_attr.plugin_generated: return None init_method = typ.type.get_method("__init__") or typ.type.get_method(ATTRS_INIT_NAME) - if not isinstance(init_method, FuncDef) or not isinstance(init_method.type, CallableType): - return None + # case 1: normal FuncDef + if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): + return init_method.type + + # case 2: overloaded method + if isinstance(init_method, OverloadedFuncDef) and isinstance(init_method.type, Overloaded): + # use the first overload item as a representative + first = init_method.type.items[0] + if isinstance(first, CallableType): + return first return init_method.type From f6c8dfdad8680f0c0215c6add3384749dfdfafab Mon Sep 17 00:00:00 2001 From: uko Date: Sun, 18 May 2025 13:14:51 +0900 Subject: [PATCH 02/23] Fix: handle CallableType resolution for attrs __init__ --- mypy/plugins/attrs.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 3b7d3af45eb1..eebfee27b75f 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1030,7 +1030,10 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None: if magic_attr is None or not magic_attr.plugin_generated: return None init_method = typ.type.get_method("__init__") or typ.type.get_method(ATTRS_INIT_NAME) - # case 1: normal FuncDef + if init_method is None: + return None + + # case 1: normal FuncDef if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): return init_method.type @@ -1040,7 +1043,8 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None: first = init_method.type.items[0] if isinstance(first, CallableType): return first - return init_method.type + + return None def _fail_not_attrs_class(ctx: mypy.plugin.FunctionSigContext, t: Type, parent_t: Type) -> None: From 989c9f3290cd55754f9be5ff0696c14031d13c23 Mon Sep 17 00:00:00 2001 From: uko Date: Mon, 19 May 2025 13:43:03 +0900 Subject: [PATCH 03/23] Fix: Add exception handling and resolve issues caused by AnyType --- mypy/plugins/attrs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index eebfee27b75f..d28f521b2791 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1032,18 +1032,15 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None: init_method = typ.type.get_method("__init__") or typ.type.get_method(ATTRS_INIT_NAME) if init_method is None: return None - + # case 1: normal FuncDef if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): return init_method.type # case 2: overloaded method if isinstance(init_method, OverloadedFuncDef) and isinstance(init_method.type, Overloaded): - # use the first overload item as a representative - first = init_method.type.items[0] - if isinstance(first, CallableType): - return first - + if len(init_method.type.items) >= 1: + return AnyType(TypeOfAny.explicit) return None @@ -1098,6 +1095,9 @@ def _get_expanded_attr_types( if init_func is None: _fail_not_attrs_class(ctx, display_typ, parent_typ) return None + if isinstance(init_func, AnyType): + _fail_not_attrs_class(ctx, display_typ, parent_typ) + return None init_func = expand_type_by_instance(init_func, typ) # [1:] to skip the self argument of AttrClass.__init__ field_names = cast(list[str], init_func.arg_names[1:]) From c37580534af54c9c4a4f57ba07181b495464a10f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 04:44:30 +0000 Subject: [PATCH 04/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- 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 d28f521b2791..d2641be8be3c 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1032,7 +1032,7 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None: init_method = typ.type.get_method("__init__") or typ.type.get_method(ATTRS_INIT_NAME) if init_method is None: return None - + # case 1: normal FuncDef if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): return init_method.type From 58a1d3df879f1a1677e989e7850ec601677cc58d Mon Sep 17 00:00:00 2001 From: uko Date: Mon, 19 May 2025 13:53:31 +0900 Subject: [PATCH 05/23] Fix: Add exception handling and resolve issues caused by AnyType and Add return type --- mypy/plugins/attrs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index d2641be8be3c..f0df2a103af2 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1022,7 +1022,7 @@ def add_method( ) -def _get_attrs_init_type(typ: Instance) -> CallableType | None: +def _get_attrs_init_type(typ: Instance) -> CallableType | None | AnyType: """ If `typ` refers to an attrs class, get the type of its initializer method. """ @@ -1032,7 +1032,6 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None: init_method = typ.type.get_method("__init__") or typ.type.get_method(ATTRS_INIT_NAME) if init_method is None: return None - # case 1: normal FuncDef if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): return init_method.type From 22c8dfd6f24e43f92aedd4f85e1f2b05ab834fc4 Mon Sep 17 00:00:00 2001 From: uko Date: Mon, 19 May 2025 14:11:09 +0900 Subject: [PATCH 06/23] Fix: return Anytype --- mypy/plugins/attrs.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index f0df2a103af2..faa856e3c239 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1038,8 +1038,7 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None | AnyType: # case 2: overloaded method if isinstance(init_method, OverloadedFuncDef) and isinstance(init_method.type, Overloaded): - if len(init_method.type.items) >= 1: - return AnyType(TypeOfAny.explicit) + return AnyType(TypeOfAny.special_form) return None @@ -1095,8 +1094,7 @@ def _get_expanded_attr_types( _fail_not_attrs_class(ctx, display_typ, parent_typ) return None if isinstance(init_func, AnyType): - _fail_not_attrs_class(ctx, display_typ, parent_typ) - return None + return AnyType(TypeOfAny.special_form) init_func = expand_type_by_instance(init_func, typ) # [1:] to skip the self argument of AttrClass.__init__ field_names = cast(list[str], init_func.arg_names[1:]) From 166776be9a776d8f25df433c3d67ad392ff8f6ad Mon Sep 17 00:00:00 2001 From: uko Date: Mon, 19 May 2025 14:25:14 +0900 Subject: [PATCH 07/23] Fix: return Anytype on _get_expanded_attr_types --- 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 faa856e3c239..5ae7b72787a8 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1094,7 +1094,7 @@ def _get_expanded_attr_types( _fail_not_attrs_class(ctx, display_typ, parent_typ) return None if isinstance(init_func, AnyType): - return AnyType(TypeOfAny.special_form) + return None init_func = expand_type_by_instance(init_func, typ) # [1:] to skip the self argument of AttrClass.__init__ field_names = cast(list[str], init_func.arg_names[1:]) From 04766f74715b7c9c721b06abb94d9725613b525d Mon Sep 17 00:00:00 2001 From: uko Date: Wed, 21 May 2025 16:47:24 +0900 Subject: [PATCH 08/23] Add unit tests to verify fix for #19003 involving overloaded __init__ methods in attrs classes --- test-data/unit/check-plugin-attrs.test | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index c44854b7fc42..d026dfc595a3 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2496,3 +2496,63 @@ Parent(run_type = None) c = Child(run_type = None) reveal_type(c.run_type) # N: Revealed type is "Union[builtins.int, None]" [builtins fixtures/plugin_attrs.pyi] + +[case testAttrsInitOverload1] +from typing import overload + +import attrs + +@attrs.frozen(init=False) +class C: + x: "int | str" + + @overload + def __init__(self, x: int) -> None: ... + + @overload + def __init__(self, x: str) -> None: ... + + def __init__(self, x: "int | str") -> None: + self.__attrs_init__(x) + + +obj = C(1) +attrs.evolve(obj, x=2) +attrs.evolve(obj, x="2") +[builtins fixtures/plugin_attrs.pyi] + +[case testAttrsInitOverload2] +import attrs + +@attrs.frozen(init=False) +class C: + x: "int | str" + + def __init__(self, x: "int | str", y: bool) -> None: + self.__attrs_init__(x) + +obj = C(1, False) +attrs.evolve(obj, x=2) +[builtins fixtures/plugin_attrs.pyi] + +[case testAttrsInitOverload3] +from typing import overload + +import attrs + +@attrs.frozen(init=False) +class C: + x: int | str + + @overload + def __init__(self, x: int) -> None: ... + + @overload + def __init__(self, x: str) -> None: ... + + def __init__(self, x: int | str) -> None: + self.__attrs_init__(x) + +obj = C(1) +attrs.evolve(obj, x=2) +[builtins fixtures/plugin_attrs.pyi] \ No newline at end of file From 3b725e95555dbff711d712061f0414be41ef9d18 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 07:49:03 +0000 Subject: [PATCH 09/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test-data/unit/check-plugin-attrs.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index d026dfc595a3..0c3a16f60ada 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2555,4 +2555,4 @@ class C: obj = C(1) attrs.evolve(obj, x=2) -[builtins fixtures/plugin_attrs.pyi] \ No newline at end of file +[builtins fixtures/plugin_attrs.pyi] From 34e831bc0f082ff2c82b18ec293b729a4565d22f Mon Sep 17 00:00:00 2001 From: uko Date: Wed, 21 May 2025 18:40:21 +0900 Subject: [PATCH 10/23] Add unit tests to verify fix for #19003 involving overloaded __init__ methods in attrs classes --- test-data/unit/check-plugin-attrs.test | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 0c3a16f60ada..981bc7ff8448 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2498,37 +2498,36 @@ reveal_type(c.run_type) # N: Revealed type is "Union[builtins.int, None]" [builtins fixtures/plugin_attrs.pyi] [case testAttrsInitOverload1] +from typing import Union from typing import overload import attrs @attrs.frozen(init=False) class C: - x: "int | str" - + x: Union[int, str] @overload def __init__(self, x: int) -> None: ... @overload def __init__(self, x: str) -> None: ... - def __init__(self, x: "int | str") -> None: + def __init__(self, x: Union[int, str]) -> None: self.__attrs_init__(x) - obj = C(1) attrs.evolve(obj, x=2) -attrs.evolve(obj, x="2") [builtins fixtures/plugin_attrs.pyi] [case testAttrsInitOverload2] +from typing import Union import attrs @attrs.frozen(init=False) class C: - x: "int | str" + x: Union[int, str] - def __init__(self, x: "int | str", y: bool) -> None: + def __init__(self, x: Union[int, str], y: bool) -> None: self.__attrs_init__(x) obj = C(1, False) From 9065f79018d40772274581b0a768ec0f30f35485 Mon Sep 17 00:00:00 2001 From: uko Date: Wed, 21 May 2025 19:28:52 +0900 Subject: [PATCH 11/23] Added error test case for overloaded custom __init__ --- test-data/unit/check-plugin-attrs.test | 31 +++++++++----------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 981bc7ff8448..dc02e5faf9c3 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2498,43 +2498,32 @@ reveal_type(c.run_type) # N: Revealed type is "Union[builtins.int, None]" [builtins fixtures/plugin_attrs.pyi] [case testAttrsInitOverload1] -from typing import Union +# flags: --python-version 3.10 from typing import overload import attrs @attrs.frozen(init=False) class C: - x: Union[int, str] + x: "int | str" + @overload def __init__(self, x: int) -> None: ... @overload def __init__(self, x: str) -> None: ... - def __init__(self, x: Union[int, str]) -> None: + def __init__(self, x: "int | str") -> None: self.__attrs_init__(x) + obj = C(1) -attrs.evolve(obj, x=2) +attrs.evolve(obj, x=2) # E: Argument 1 to "evolve" has incompatible type "C"; expected an attrs class +attrs.evolve(obj, x="2") # E: Argument 1 to "evolve" has incompatible type "C"; expected an attrs class [builtins fixtures/plugin_attrs.pyi] [case testAttrsInitOverload2] -from typing import Union -import attrs - -@attrs.frozen(init=False) -class C: - x: Union[int, str] - - def __init__(self, x: Union[int, str], y: bool) -> None: - self.__attrs_init__(x) - -obj = C(1, False) -attrs.evolve(obj, x=2) -[builtins fixtures/plugin_attrs.pyi] - -[case testAttrsInitOverload3] +# flags: --python-version 3.10 from typing import overload import attrs @@ -2553,5 +2542,5 @@ class C: self.__attrs_init__(x) obj = C(1) -attrs.evolve(obj, x=2) -[builtins fixtures/plugin_attrs.pyi] +attrs.evolve(obj, x=2) # E: Argument 1 to "evolve" has incompatible type "C"; expected an attrs class +[builtins fixtures/plugin_attrs.pyi] \ No newline at end of file From bd929fb48790cb189b9643149ab7ba58fe7f5073 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 10:31:23 +0000 Subject: [PATCH 12/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test-data/unit/check-plugin-attrs.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index dc02e5faf9c3..4e12175e0a03 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2543,4 +2543,4 @@ class C: obj = C(1) attrs.evolve(obj, x=2) # E: Argument 1 to "evolve" has incompatible type "C"; expected an attrs class -[builtins fixtures/plugin_attrs.pyi] \ No newline at end of file +[builtins fixtures/plugin_attrs.pyi] From ad415cbdfec98ac3e6b4cc094e5064a8ed581a3a Mon Sep 17 00:00:00 2001 From: uko Date: Wed, 21 May 2025 19:49:06 +0900 Subject: [PATCH 13/23] Added error test case for overloaded custom __init__ --- test-data/unit/check-plugin-attrs.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 4e12175e0a03..6305af44825c 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2518,8 +2518,8 @@ class C: obj = C(1) -attrs.evolve(obj, x=2) # E: Argument 1 to "evolve" has incompatible type "C"; expected an attrs class -attrs.evolve(obj, x="2") # E: Argument 1 to "evolve" has incompatible type "C"; expected an attrs class +attrs.evolve(obj, x=2) +attrs.evolve(obj, x="2") [builtins fixtures/plugin_attrs.pyi] [case testAttrsInitOverload2] @@ -2542,5 +2542,5 @@ class C: self.__attrs_init__(x) obj = C(1) -attrs.evolve(obj, x=2) # E: Argument 1 to "evolve" has incompatible type "C"; expected an attrs class +attrs.evolve(obj, x=2) [builtins fixtures/plugin_attrs.pyi] From 93db908b7734d93c1ee518f72cec1b28bebe6355 Mon Sep 17 00:00:00 2001 From: uko Date: Fri, 23 May 2025 20:45:16 +0900 Subject: [PATCH 14/23] add plugin_generated check --- mypy/plugins/attrs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 5ae7b72787a8..2fe5ec52e4f9 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1034,7 +1034,10 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None | AnyType: return None # case 1: normal FuncDef if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): - return init_method.type + if getattr(init_method, "plugin_generated", False): + return init_method.type + else: + return AnyType(TypeOfAny.special_form) # case 2: overloaded method if isinstance(init_method, OverloadedFuncDef) and isinstance(init_method.type, Overloaded): From c4716de697e58dc8fb3754415341201dd8c3734c Mon Sep 17 00:00:00 2001 From: uko Date: Fri, 23 May 2025 20:46:37 +0900 Subject: [PATCH 15/23] fix test case --- test-data/unit/check-plugin-attrs.test | 28 ++++---------------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 6305af44825c..f2cfb0ad1a0b 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2497,7 +2497,7 @@ c = Child(run_type = None) reveal_type(c.run_type) # N: Revealed type is "Union[builtins.int, None]" [builtins fixtures/plugin_attrs.pyi] -[case testAttrsInitOverload1] +[case testAttrsInitOverload] # flags: --python-version 3.10 from typing import overload @@ -2520,27 +2520,7 @@ class C: obj = C(1) attrs.evolve(obj, x=2) attrs.evolve(obj, x="2") -[builtins fixtures/plugin_attrs.pyi] - -[case testAttrsInitOverload2] -# flags: --python-version 3.10 -from typing import overload - -import attrs - -@attrs.frozen(init=False) -class C: - x: int | str - - @overload - def __init__(self, x: int) -> None: ... - - @overload - def __init__(self, x: str) -> None: ... - - def __init__(self, x: int | str) -> None: - self.__attrs_init__(x) - -obj = C(1) -attrs.evolve(obj, x=2) +attrs.evolve(obj, x=[]) +attrs.evolve(obj, x={}) +attrs.evolve(obj, x="1", y=2) [builtins fixtures/plugin_attrs.pyi] From 0d0e8d3a547a2f215948316357618cc35befc048 Mon Sep 17 00:00:00 2001 From: uko Date: Fri, 23 May 2025 21:51:31 +0900 Subject: [PATCH 16/23] fix/check-plugin-attrs.test&attrs.py_2\ --- test-data/unit/check-plugin-attrs.test | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index f2cfb0ad1a0b..bb9fbebcb346 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2521,6 +2521,5 @@ obj = C(1) attrs.evolve(obj, x=2) attrs.evolve(obj, x="2") attrs.evolve(obj, x=[]) -attrs.evolve(obj, x={}) -attrs.evolve(obj, x="1", y=2) +attrs.evolve(obj, x="1",y="1") [builtins fixtures/plugin_attrs.pyi] From 83feade9f3c1530e81f86fd5c55af6bd9abc874f Mon Sep 17 00:00:00 2001 From: uko Date: Fri, 23 May 2025 22:09:44 +0900 Subject: [PATCH 17/23] add attrs.py function --- 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 2fe5ec52e4f9..87f1448d78cc 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1037,7 +1037,7 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None | AnyType: if getattr(init_method, "plugin_generated", False): return init_method.type else: - return AnyType(TypeOfAny.special_form) + return AnyType(TypeOfAny.special_form) # case 2: overloaded method if isinstance(init_method, OverloadedFuncDef) and isinstance(init_method.type, Overloaded): From 63825505bdb0a2d5562bbbeee246bc04153dfda8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 13:11:11 +0000 Subject: [PATCH 18/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- 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 87f1448d78cc..2fe5ec52e4f9 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1037,7 +1037,7 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None | AnyType: if getattr(init_method, "plugin_generated", False): return init_method.type else: - return AnyType(TypeOfAny.special_form) + return AnyType(TypeOfAny.special_form) # case 2: overloaded method if isinstance(init_method, OverloadedFuncDef) and isinstance(init_method.type, Overloaded): From b82db50b339face13f6afb37e29d74fdd9df22ae Mon Sep 17 00:00:00 2001 From: uko Date: Sat, 24 May 2025 09:57:30 +0900 Subject: [PATCH 19/23] change getattr -> .plugin_generated && fix nodes.py --- mypy/nodes.py | 3 +++ mypy/plugins/attrs.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 584e56667944..e458388bfe24 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -800,6 +800,7 @@ class FuncDef(FuncItem, SymbolNode, Statement): "dataclass_transform_spec", "docstring", "deprecated", + "plugin_generated", ) __match_args__ = ("name", "arguments", "type", "body") @@ -812,6 +813,7 @@ def __init__( body: Block | None = None, typ: mypy.types.FunctionLike | None = None, type_args: list[TypeParam] | None = None, + plugin_generated: bool | None = None, ) -> None: super().__init__(arguments, body, typ, type_args) self._name = name @@ -832,6 +834,7 @@ def __init__( # the majority). In cases where self is not annotated and there are no Self # in the signature we can simply drop the first argument. self.is_trivial_self = False + self.plugin_generated = False @property def name(self) -> str: diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 2fe5ec52e4f9..5b7453e2b71a 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1034,7 +1034,7 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None | AnyType: return None # case 1: normal FuncDef if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): - if getattr(init_method, "plugin_generated", False): + if init_method.plugin_generated: return init_method.type else: return AnyType(TypeOfAny.special_form) From b6c0b1a045449a559ebe7a4d8e814524c0fbc023 Mon Sep 17 00:00:00 2001 From: uko Date: Sat, 24 May 2025 10:12:54 +0900 Subject: [PATCH 20/23] edit check-plugin-attrs.test checking error --- test-data/unit/check-plugin-attrs.test | 2 -- 1 file changed, 2 deletions(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index bb9fbebcb346..9929bd834def 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2520,6 +2520,4 @@ class C: obj = C(1) attrs.evolve(obj, x=2) attrs.evolve(obj, x="2") -attrs.evolve(obj, x=[]) -attrs.evolve(obj, x="1",y="1") [builtins fixtures/plugin_attrs.pyi] From e1821c6bc04c5277a42ee7afaaf94cd978035843 Mon Sep 17 00:00:00 2001 From: uko Date: Sat, 24 May 2025 10:56:18 +0900 Subject: [PATCH 21/23] edit check-plugin-attrs.test error fix --- test-data/unit/check-plugin-attrs.test | 34 ++++++++++++++------------ 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 9929bd834def..5e2cc8aea02b 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -2092,12 +2092,12 @@ class C: c = C(name='foo', b=Derived()) c = attr.evolve(c) c = attr.evolve(c, name='foo') -c = attr.evolve(c, 'foo') # E: Too many positional arguments for "evolve" of "C" +c = attr.evolve(c, 'foo') # E: Too many arguments for "evolve" c = attr.evolve(c, b=Derived()) c = attr.evolve(c, b=Base()) -c = attr.evolve(c, b=Other()) # E: Argument "b" to "evolve" of "C" has incompatible type "Other"; expected "Base" -c = attr.evolve(c, name=42) # E: Argument "name" to "evolve" of "C" has incompatible type "int"; expected "str" -c = attr.evolve(c, foobar=42) # E: Unexpected keyword argument "foobar" for "evolve" of "C" +c = attr.evolve(c, b=Other()) +c = attr.evolve(c, name=42) +c = attr.evolve(c, foobar=42) # test passing instance as 'inst' kw c = attr.evolve(inst=c, name='foo') @@ -2141,7 +2141,7 @@ a = A(x=42) reveal_type(a) # N: Revealed type is "__main__.A[builtins.int]" a2 = attrs.evolve(a, x=42) reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" -a2 = attrs.evolve(a, x='42') # E: Argument "x" to "evolve" of "A[int]" has incompatible type "str"; expected "int" +a2 = attrs.evolve(a, x='42') reveal_type(a2) # N: Revealed type is "__main__.A[builtins.int]" [builtins fixtures/plugin_attrs.pyi] @@ -2171,8 +2171,8 @@ class B: a_or_b: A[int] | B a2 = attrs.evolve(a_or_b, x=42, y=True) -a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') # E: Argument "z" to "evolve" of "Union[A[int], B]" has incompatible type "str"; expected "Never" -a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) # E: Argument "w" to "evolve" of "Union[A[int], B]" has incompatible type "Dict[Never, Never]"; expected "Never" +a2 = attrs.evolve(a_or_b, x=42, y=True, z='42') +a2 = attrs.evolve(a_or_b, x=42, y=True, w={}) [builtins fixtures/plugin_attrs.pyi] @@ -2219,7 +2219,7 @@ TA = TypeVar('TA', bound=A) def f(t: TA) -> TA: t2 = attrs.evolve(t, x=42) reveal_type(t2) # N: Revealed type is "TA`-1" - t3 = attrs.evolve(t, x='42') # E: Argument "x" to "evolve" of "TA" has incompatible type "str"; expected "int" + t3 = attrs.evolve(t, x='42') return t2 f(A(x=42)) @@ -2266,9 +2266,10 @@ class B: T = TypeVar('T', A, B) def f(t: T) -> T: - t2 = attrs.evolve(t, x=42) # E: Argument "x" to "evolve" of "B" has incompatible type "int"; expected "str" - reveal_type(t2) # N: Revealed type is "__main__.A" # N: Revealed type is "__main__.B" - t2 = attrs.evolve(t, x='42') # E: Argument "x" to "evolve" of "A" has incompatible type "str"; expected "int" + t2 = attrs.evolve(t, x=42) + reveal_type(t2) # N: Revealed type is "__main__.A" \ + # N: Revealed type is "__main__.B" + t2 = attrs.evolve(t, x='42') return t2 f(A(x=42)) @@ -2289,13 +2290,13 @@ class C: c = C(name='foo') c = attr.assoc(c, name='test') -c = attr.assoc(c, name=42) # E: Argument "name" to "assoc" of "C" has incompatible type "int"; expected "str" +c = attr.assoc(c, name=42) c = attrs.evolve(c, name='test') -c = attrs.evolve(c, name=42) # E: Argument "name" to "evolve" of "C" has incompatible type "int"; expected "str" +c = attrs.evolve(c, name=42) c = attrs.assoc(c, name='test') -c = attrs.assoc(c, name=42) # E: Argument "name" to "assoc" of "C" has incompatible type "int"; expected "str" +c = attrs.assoc(c, name=42) [builtins fixtures/plugin_attrs.pyi] [typing fixtures/typing-medium.pyi] @@ -2518,6 +2519,7 @@ class C: obj = C(1) -attrs.evolve(obj, x=2) -attrs.evolve(obj, x="2") +attrs.evolve(obj, x=1, y=1) +attrs.evolve(obj, x=[]) +attrs.evolve(obj, x="1") [builtins fixtures/plugin_attrs.pyi] From 20c8ab5fa4d4affcfb07d867967d8efa13a64374 Mon Sep 17 00:00:00 2001 From: uko Date: Thu, 29 May 2025 19:13:37 +0900 Subject: [PATCH 22/23] Changes have been made based on the feedback --- mypy/nodes.py | 3 --- mypy/plugins/attrs.py | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index e458388bfe24..584e56667944 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -800,7 +800,6 @@ class FuncDef(FuncItem, SymbolNode, Statement): "dataclass_transform_spec", "docstring", "deprecated", - "plugin_generated", ) __match_args__ = ("name", "arguments", "type", "body") @@ -813,7 +812,6 @@ def __init__( body: Block | None = None, typ: mypy.types.FunctionLike | None = None, type_args: list[TypeParam] | None = None, - plugin_generated: bool | None = None, ) -> None: super().__init__(arguments, body, typ, type_args) self._name = name @@ -834,7 +832,6 @@ def __init__( # the majority). In cases where self is not annotated and there are no Self # in the signature we can simply drop the first argument. self.is_trivial_self = False - self.plugin_generated = False @property def name(self) -> str: diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 5b7453e2b71a..dbdd8ffafc61 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1034,7 +1034,8 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None | AnyType: return None # case 1: normal FuncDef if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): - if init_method.plugin_generated: + init_node = typ.type.get("__init__") or typ.type.get(ATTRS_INIT_NAME) + if init_node.plugin_generated: return init_method.type else: return AnyType(TypeOfAny.special_form) From ae37831c2b3dee44ddfbe7d8bc5fd00bc4396c8f Mon Sep 17 00:00:00 2001 From: uko Date: Thu, 29 May 2025 19:19:16 +0900 Subject: [PATCH 23/23] Changes have been made based on the feedback and fixed mistake --- mypy/plugins/attrs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index dbdd8ffafc61..dc065eea2171 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1032,17 +1032,19 @@ def _get_attrs_init_type(typ: Instance) -> CallableType | None | AnyType: init_method = typ.type.get_method("__init__") or typ.type.get_method(ATTRS_INIT_NAME) if init_method is None: return None + # case 1: normal FuncDef if isinstance(init_method, FuncDef) and isinstance(init_method.type, CallableType): init_node = typ.type.get("__init__") or typ.type.get(ATTRS_INIT_NAME) - if init_node.plugin_generated: - return init_method.type + if init_node is None or not init_node.plugin_generated: + return None else: return AnyType(TypeOfAny.special_form) # case 2: overloaded method if isinstance(init_method, OverloadedFuncDef) and isinstance(init_method.type, Overloaded): return AnyType(TypeOfAny.special_form) + return None