Skip to content

Commit

Permalink
attrs: remove fields type check (#15983)
Browse files Browse the repository at this point in the history
Since python-attrs/attrs#890 (≥ 22.1.0)
`attrs.fields` is typed to accept a protocol.
Since python-attrs/attrs#997 (≥ 22.2.0)
`attrs.has` is a type-guard.

Support both by removing the explicit error reporting and letting it
fall through to the type stub.

Fixes #15980.
  • Loading branch information
ikonst authored Aug 30, 2023
1 parent 379b52f commit 2298829
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 14 deletions.
5 changes: 0 additions & 5 deletions mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 10 additions & 6 deletions test-data/unit/check-plugin-attrs.test
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]

Expand Down
13 changes: 10 additions & 3 deletions test-data/unit/lib-stub/attrs/__init__.pyi
Original file line number Diff line number Diff line change
@@ -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)

Expand Down Expand Up @@ -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: ...

0 comments on commit 2298829

Please sign in to comment.