diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 3d326a5f4e80..6f5b6f35da07 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1111,9 +1111,4 @@ def fields_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl assert ret_type is not None return ctx.default_signature.copy_modified(arg_types=arg_types, ret_type=ret_type) - ctx.api.fail( - f'Argument 1 to "fields" has incompatible type "{format_type_bare(proper_type, ctx.api.options)}"; expected an attrs class', - ctx.context, - ) - return ctx.default_signature diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test index e8598132c50e..1465bab2bb7b 100644 --- a/test-data/unit/check-plugin-attrs.test +++ b/test-data/unit/check-plugin-attrs.test @@ -1596,16 +1596,18 @@ def f(t: TA) -> None: [builtins fixtures/plugin_attrs.pyi] [case testNonattrsFields] -# flags: --no-strict-optional from typing import Any, cast, Type -from attrs import fields +from attrs import fields, has class A: b: int c: str -fields(A) # E: Argument 1 to "fields" has incompatible type "Type[A]"; expected an attrs class -fields(None) # E: Argument 1 to "fields" has incompatible type "None"; expected an attrs class +if has(A): + fields(A) +else: + fields(A) # E: Argument 1 to "fields" has incompatible type "Type[A]"; expected "Type[AttrsInstance]" +fields(None) # E: Argument 1 to "fields" has incompatible type "None"; expected "Type[AttrsInstance]" fields(cast(Any, 42)) fields(cast(Type[Any], 43)) @@ -2167,7 +2169,8 @@ TA = TypeVar('TA', bound=A) TB = TypeVar('TB', bound=B) def f(b_or_t: TA | TB | int) -> None: - a2 = attrs.evolve(b_or_t) # E: Argument 1 to "evolve" has type "Union[TA, TB, int]" whose item "TB" is not bound to an attrs class # E: Argument 1 to "evolve" has incompatible type "Union[TA, TB, int]" whose item "int" is not an attrs class + a2 = attrs.evolve(b_or_t) # E: Argument 1 to "evolve" has type "Union[TA, TB, int]" whose item "TB" is not bound to an attrs class \ + # E: Argument 1 to "evolve" has incompatible type "Union[TA, TB, int]" whose item "int" is not an attrs class [builtins fixtures/plugin_attrs.pyi] @@ -2216,7 +2219,8 @@ def h(t: TNone) -> None: _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has a variable type "TNone" not bound to an attrs class def x(t: TUnion) -> None: - _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has incompatible type "TUnion" whose item "str" is not an attrs class # E: Argument 1 to "evolve" has incompatible type "TUnion" whose item "int" is not an attrs class + _ = attrs.evolve(t, x=42) # E: Argument 1 to "evolve" has incompatible type "TUnion" whose item "str" is not an attrs class \ + # E: Argument 1 to "evolve" has incompatible type "TUnion" whose item "int" is not an attrs class [builtins fixtures/plugin_attrs.pyi] diff --git a/test-data/unit/lib-stub/attrs/__init__.pyi b/test-data/unit/lib-stub/attrs/__init__.pyi index a575f97da9bc..7a88170d7271 100644 --- a/test-data/unit/lib-stub/attrs/__init__.pyi +++ b/test-data/unit/lib-stub/attrs/__init__.pyi @@ -1,7 +1,14 @@ -from typing import TypeVar, overload, Callable, Any, Optional, Union, Sequence, Mapping, Generic +from typing import TypeVar, overload, Callable, Any, Optional, Union, Sequence, Mapping, \ + Protocol, ClassVar, Type +from typing_extensions import TypeGuard from attr import Attribute as Attribute + +class AttrsInstance(Protocol): + __attrs_attrs__: ClassVar[Any] + + _T = TypeVar('_T') _C = TypeVar('_C', bound=type) @@ -131,5 +138,5 @@ def field( def evolve(inst: _T, **changes: Any) -> _T: ... def assoc(inst: _T, **changes: Any) -> _T: ... - -def fields(cls: type) -> Any: ... +def has(cls: type) -> TypeGuard[Type[AttrsInstance]]: ... +def fields(cls: Type[AttrsInstance]) -> Any: ...