Skip to content

Commit

Permalink
Recognise both attrs and attr package names in the plugin (#12469)
Browse files Browse the repository at this point in the history
Recognise new 'attrs' package name in the attrs plugin
  • Loading branch information
TeamSpen210 authored Mar 29, 2022
1 parent 7ac5a19 commit 433544b
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 3 deletions.
8 changes: 5 additions & 3 deletions mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,16 @@
attr_dataclass_makers: Final = {
'attr.dataclass',
}
attr_frozen_makers: Final = {"attr.frozen"}
attr_define_makers: Final = {"attr.define", "attr.mutable"}
attr_frozen_makers: Final = {"attr.frozen", "attrs.frozen"}
attr_define_makers: Final = {"attr.define", "attr.mutable", "attrs.define", "attrs.mutable"}
attr_attrib_makers: Final = {
'attr.ib',
'attr.attrib',
'attr.attr',
'attr.field',
'attrs.field',
}
attr_optional_converters: Final = {'attr.converters.optional', 'attrs.converters.optional'}

SELF_TVAR_NAME: Final = "_AT"
MAGIC_ATTR_NAME: Final = "__attrs_attrs__"
Expand Down Expand Up @@ -609,7 +611,7 @@ def _parse_converter(ctx: 'mypy.plugin.ClassDefContext',

if (isinstance(converter, CallExpr)
and isinstance(converter.callee, RefExpr)
and converter.callee.fullname == "attr.converters.optional"
and converter.callee.fullname in attr_optional_converters
and converter.args
and converter.args[0]):
# Special handling for attr.converters.optional(type)
Expand Down
36 changes: 36 additions & 0 deletions test-data/unit/check-attr.test
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,22 @@ reveal_type(D) # N: Revealed type is "def (b: Any) -> __main__.D"

[builtins fixtures/bool.pyi]

[case testAttrsNewPackage]
import attrs
@attrs.define
class A:
a: int = attrs.field()
b: bool

@attrs.frozen
class B:
a: bool
b: int

reveal_type(A) # N: Revealed type is "def (a: builtins.int, b: builtins.bool) -> __main__.A"
reveal_type(B) # N: Revealed type is "def (a: builtins.bool, b: builtins.int) -> __main__.B"

[builtins fixtures/bool.pyi]

[case testAttrsDataClass]
import attr
Expand Down Expand Up @@ -1133,6 +1148,27 @@ A(None, None)

[builtins fixtures/attr.pyi]

[case testAttrsOptionalConverterNewPackage]
# flags: --strict-optional
import attrs
from attrs.converters import optional
from typing import Optional

def converter(s:int) -> str:
return 'hello'


@attrs.define
class A:
y: Optional[int] = attrs.field(converter=optional(int))
z: Optional[str] = attrs.field(converter=optional(converter))


A(None, None)

[builtins fixtures/attr.pyi]


[case testAttrsTypeVarNoCollision]
from typing import TypeVar, Generic
import attr
Expand Down
128 changes: 128 additions & 0 deletions test-data/unit/lib-stub/attrs/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from typing import TypeVar, overload, Callable, Any, Optional, Union, Sequence, Mapping

_T = TypeVar('_T')
_C = TypeVar('_C', bound=type)

_ValidatorType = Callable[[Any, Any, _T], Any]
_ConverterType = Callable[[Any], _T]
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]

@overload
def define(
maybe_cls: _C,
*,
these: Optional[Mapping[str, Any]] = ...,
repr: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
slots: bool = ...,
frozen: bool = ...,
weakref_slot: bool = ...,
str: bool = ...,
auto_attribs: bool = ...,
kw_only: bool = ...,
cache_hash: bool = ...,
auto_exc: bool = ...,
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
auto_detect: bool = ...,
getstate_setstate: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
) -> _C: ...
@overload
def define(
maybe_cls: None = ...,
*,
these: Optional[Mapping[str, Any]] = ...,
repr: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
slots: bool = ...,
frozen: bool = ...,
weakref_slot: bool = ...,
str: bool = ...,
auto_attribs: bool = ...,
kw_only: bool = ...,
cache_hash: bool = ...,
auto_exc: bool = ...,
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
auto_detect: bool = ...,
getstate_setstate: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
) -> Callable[[_C], _C]: ...

mutable = define
frozen = define # they differ only in their defaults

@overload
def field(
*,
default: None = ...,
validator: None = ...,
repr: object = ...,
hash: Optional[bool] = ...,
init: bool = ...,
metadata: Optional[Mapping[Any, Any]] = ...,
converter: None = ...,
factory: None = ...,
kw_only: bool = ...,
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[_OnSetAttrArgType] = ...,
) -> Any: ...

# This form catches an explicit None or no default and infers the type from the
# other arguments.
@overload
def field(
*,
default: None = ...,
validator: Optional[_ValidatorArgType[_T]] = ...,
repr: object = ...,
hash: Optional[bool] = ...,
init: bool = ...,
metadata: Optional[Mapping[Any, Any]] = ...,
converter: Optional[_ConverterType] = ...,
factory: Optional[Callable[[], _T]] = ...,
kw_only: bool = ...,
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
) -> _T: ...

# This form catches an explicit default argument.
@overload
def field(
*,
default: _T,
validator: Optional[_ValidatorArgType[_T]] = ...,
repr: object = ...,
hash: Optional[bool] = ...,
init: bool = ...,
metadata: Optional[Mapping[Any, Any]] = ...,
converter: Optional[_ConverterType] = ...,
factory: Optional[Callable[[], _T]] = ...,
kw_only: bool = ...,
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
) -> _T: ...

# This form covers type=non-Type: e.g. forward references (str), Any
@overload
def field(
*,
default: Optional[_T] = ...,
validator: Optional[_ValidatorArgType[_T]] = ...,
repr: object = ...,
hash: Optional[bool] = ...,
init: bool = ...,
metadata: Optional[Mapping[Any, Any]] = ...,
converter: Optional[_ConverterType] = ...,
factory: Optional[Callable[[], _T]] = ...,
kw_only: bool = ...,
eq: Optional[bool] = ...,
order: Optional[bool] = ...,
on_setattr: Optional[object] = ...,
) -> Any: ...
12 changes: 12 additions & 0 deletions test-data/unit/lib-stub/attrs/converters.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import TypeVar, Optional, Callable, overload
from attr import _ConverterType

_T = TypeVar("_T")

def optional(
converter: _ConverterType[_T]
) -> _ConverterType[Optional[_T]]: ...
@overload
def default_if_none(default: _T) -> _ConverterType[_T]: ...
@overload
def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType[_T]: ...

0 comments on commit 433544b

Please sign in to comment.