diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index b7b3821576ea..86dc27cb72d0 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. """ @@ -1030,9 +1030,18 @@ 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): + if init_method is None: return None - return init_method.type + + # 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): + return AnyType(TypeOfAny.special_form) + + return None def _fail_not_attrs_class(ctx: mypy.plugin.FunctionSigContext, t: Type, parent_t: Type) -> None: @@ -1086,6 +1095,8 @@ 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): + 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:]) diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index 6415b5104296..1375329add31 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -31,8 +31,10 @@ class A: reveal_type(A) # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A" A(1, [2]) A(1, [2], '3', 4) -A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" +A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" \ + # E: Argument 3 to "A" has incompatible type "int"; expected "str" A(1, [2], '3', 4, 5) # E: Too many arguments for "A" + [builtins fixtures/list.pyi] [case testAttrsTypeComments] @@ -49,8 +51,10 @@ class A: reveal_type(A) # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A" A(1, [2]) A(1, [2], '3', 4) -A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" +A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" \ + # E: Argument 3 to "A" has incompatible type "int"; expected "str" A(1, [2], '3', 4, 5) # E: Too many arguments for "A" + [builtins fixtures/list.pyi] [case testAttrsAutoAttribs] @@ -67,8 +71,10 @@ class A: reveal_type(A) # N: Revealed type is "def (a: builtins.int, b: builtins.list[builtins.int], c: builtins.str =, d: builtins.int =) -> __main__.A" A(1, [2]) A(1, [2], '3', 4) -A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" # E: Argument 3 to "A" has incompatible type "int"; expected "str" +A(1, 2, 3, 4) # E: Argument 2 to "A" has incompatible type "int"; expected "list[int]" \ + # E: Argument 3 to "A" has incompatible type "int"; expected "str" A(1, [2], '3', 4, 5) # E: Too many arguments for "A" + [builtins fixtures/list.pyi] [case testAttrsUntypedNoUntypedDefs] @@ -1577,7 +1583,9 @@ takes_attrs_cls(A) takes_attrs_instance(A(1, "")) takes_attrs_cls(A(1, "")) # E: Argument 1 to "takes_attrs_cls" has incompatible type "A"; expected "type[AttrsInstance]" -takes_attrs_instance(A) # E: Argument 1 to "takes_attrs_instance" has incompatible type "type[A]"; expected "AttrsInstance" # N: ClassVar protocol member AttrsInstance.__attrs_attrs__ can never be matched by a class object +takes_attrs_instance(A) # E: Argument 1 to "takes_attrs_instance" has incompatible type "type[A]"; expected "AttrsInstance" \ + # N: ClassVar protocol member AttrsInstance.__attrs_attrs__ can never be matched by a class object + [builtins fixtures/plugin_attrs.pyi] [case testAttrsFields] @@ -2496,3 +2504,29 @@ 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 testAttrsInitOverload] +# 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=1, y=1) +attrs.evolve(obj, x=[]) +attrs.evolve(obj, x="1") +[builtins fixtures/plugin_attrs.pyi]