Skip to content

Commit bb13ecc

Browse files
eurestiilevkivskyi
authored andcommitted
Support factory= in attr plugin. (#5005)
This is a new feature in attrs 18.1 The implementation is just like default=
1 parent 874d67e commit bb13ecc

File tree

3 files changed

+120
-45
lines changed

3 files changed

+120
-45
lines changed

mypy/plugins/attrs.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,12 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
340340
init = _get_bool_argument(ctx, rvalue, 'init', True)
341341
# TODO: Check for attr.NOTHING
342342
attr_has_default = bool(_get_argument(rvalue, 'default'))
343+
attr_has_factory = bool(_get_argument(rvalue, 'factory'))
344+
345+
if attr_has_default and attr_has_factory:
346+
ctx.api.fail("Can't pass both `default` and `factory`.", rvalue)
347+
elif attr_has_factory:
348+
attr_has_default = True
343349

344350
# If the type isn't set through annotation but is passed through `type=` use that.
345351
type_arg = _get_argument(rvalue, 'type')

test-data/unit/check-attr.test

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,3 +815,32 @@ class FFrozen(F):
815815
def bar(self) -> bool:
816816
return self._cb(5, 6)
817817
[builtins fixtures/callable.pyi]
818+
819+
[case testAttrsWithFactory]
820+
from typing import List
821+
import attr
822+
def my_factory() -> int:
823+
return 7
824+
@attr.s
825+
class A:
826+
x: List[int] = attr.ib(factory=list)
827+
y: int = attr.ib(factory=my_factory)
828+
A()
829+
[builtins fixtures/list.pyi]
830+
831+
[case testAttrsFactoryAndDefault]
832+
import attr
833+
@attr.s
834+
class A:
835+
x: int = attr.ib(factory=int, default=7) # E: Can't pass both `default` and `factory`.
836+
[builtins fixtures/bool.pyi]
837+
838+
[case testAttrsFactoryBadReturn]
839+
import attr
840+
def my_factory() -> int:
841+
return 7
842+
@attr.s
843+
class A:
844+
x: int = attr.ib(factory=list) # E: Incompatible types in assignment (expression has type "List[T]", variable has type "int")
845+
y: str = attr.ib(factory=my_factory) # E: Incompatible types in assignment (expression has type "int", variable has type "str")
846+
[builtins fixtures/list.pyi]

test-data/unit/lib-stub/attr.pyi

Lines changed: 85 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,97 @@
1-
from typing import TypeVar, overload, Callable, Any, Type, Optional
1+
from typing import TypeVar, overload, Callable, Any, Type, Optional, Union, Sequence, Mapping
22

33
_T = TypeVar('_T')
44
_C = TypeVar('_C', bound=type)
55

6+
_ValidatorType = Callable[[Any, Any, _T], Any]
7+
_ConverterType = Callable[[Any], _T]
8+
_FilterType = Callable[[Any, Any], bool]
9+
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
10+
11+
# This form catches an explicit None or no default and infers the type from the other arguments.
12+
@overload
13+
def attrib(default: None = ...,
14+
validator: Optional[_ValidatorArgType[_T]] = ...,
15+
repr: bool = ...,
16+
cmp: bool = ...,
17+
hash: Optional[bool] = ...,
18+
init: bool = ...,
19+
convert: Optional[_ConverterType[_T]] = ...,
20+
metadata: Optional[Mapping[Any, Any]] = ...,
21+
type: Optional[Type[_T]] = ...,
22+
converter: Optional[_ConverterType[_T]] = ...,
23+
factory: Optional[Callable[[], _T]] = ...,
24+
) -> _T: ...
25+
# This form catches an explicit default argument.
626
@overload
7-
def attr(default: Optional[_T] = ...,
8-
validator: Optional[Any] = ...,
9-
repr: bool = ...,
10-
cmp: bool = ...,
11-
hash: Optional[bool] = ...,
12-
init: bool = ...,
13-
convert: Optional[Callable[[Any], _T]] = ...,
14-
metadata: Any = ...,
15-
type: Optional[Type[_T]] = ...,
16-
converter: Optional[Callable[[Any], _T]] = ...) -> _T: ...
27+
def attrib(default: _T,
28+
validator: Optional[_ValidatorArgType[_T]] = ...,
29+
repr: bool = ...,
30+
cmp: bool = ...,
31+
hash: Optional[bool] = ...,
32+
init: bool = ...,
33+
convert: Optional[_ConverterType[_T]] = ...,
34+
metadata: Optional[Mapping[Any, Any]] = ...,
35+
type: Optional[Type[_T]] = ...,
36+
converter: Optional[_ConverterType[_T]] = ...,
37+
factory: Optional[Callable[[], _T]] = ...,
38+
) -> _T: ...
39+
# This form catches explicit None or no default but with no other arguments returns Any.
1740
@overload
18-
def attr(default: None = ...,
19-
validator: None = ...,
20-
repr: bool = ...,
21-
cmp: bool = ...,
22-
hash: Optional[bool] = ...,
23-
init: bool = ...,
24-
convert: Optional[Callable[[Any], _T]] = ...,
25-
metadata: Any = ...,
26-
type: None = ...,
27-
converter: None = ...) -> Any: ...
41+
def attrib(default: None = ...,
42+
validator: None = ...,
43+
repr: bool = ...,
44+
cmp: bool = ...,
45+
hash: Optional[bool] = ...,
46+
init: bool = ...,
47+
convert: None = ...,
48+
metadata: Optional[Mapping[Any, Any]] = ...,
49+
type: None = ...,
50+
converter: None = ...,
51+
factory: None = ...,
52+
) -> Any: ...
53+
# This form covers type=non-Type: e.g. forward references (str), Any
54+
@overload
55+
def attrib(default: Optional[_T] = ...,
56+
validator: Optional[_ValidatorArgType[_T]] = ...,
57+
repr: bool = ...,
58+
cmp: bool = ...,
59+
hash: Optional[bool] = ...,
60+
init: bool = ...,
61+
convert: Optional[_ConverterType[_T]] = ...,
62+
metadata: Optional[Mapping[Any, Any]] = ...,
63+
type: object = ...,
64+
converter: Optional[_ConverterType[_T]] = ...,
65+
factory: Optional[Callable[[], _T]] = ...,
66+
) -> Any: ...
2867

2968
@overload
30-
def attributes(maybe_cls: _C,
31-
these: Optional[Any] = ...,
32-
repr_ns: Optional[str] = ...,
33-
repr: bool = ...,
34-
cmp: bool = ...,
35-
hash: Optional[bool] = ...,
36-
init: bool = ...,
37-
slots: bool = ...,
38-
frozen: bool = ...,
39-
str: bool = ...,
40-
auto_attribs: bool = ...) -> _C: ...
69+
def attrs(maybe_cls: _C,
70+
these: Optional[Mapping[str, Any]] = ...,
71+
repr_ns: Optional[str] = ...,
72+
repr: bool = ...,
73+
cmp: bool = ...,
74+
hash: Optional[bool] = ...,
75+
init: bool = ...,
76+
slots: bool = ...,
77+
frozen: bool = ...,
78+
str: bool = ...,
79+
auto_attribs: bool = ...) -> _C: ...
4180
@overload
42-
def attributes(maybe_cls: None = ...,
43-
these: Optional[Any] = ...,
44-
repr_ns: Optional[str] = ...,
45-
repr: bool = ...,
46-
cmp: bool = ...,
47-
hash: Optional[bool] = ...,
48-
init: bool = ...,
49-
slots: bool = ...,
50-
frozen: bool = ...,
51-
str: bool = ...,
52-
auto_attribs: bool = ...) -> Callable[[_C], _C]: ...
81+
def attrs(maybe_cls: None = ...,
82+
these: Optional[Mapping[str, Any]] = ...,
83+
repr_ns: Optional[str] = ...,
84+
repr: bool = ...,
85+
cmp: bool = ...,
86+
hash: Optional[bool] = ...,
87+
init: bool = ...,
88+
slots: bool = ...,
89+
frozen: bool = ...,
90+
str: bool = ...,
91+
auto_attribs: bool = ...) -> Callable[[_C], _C]: ...
92+
5393

5494
# aliases
55-
s = attrs = attributes
56-
ib = attrib = attr
95+
s = attributes = attrs
96+
ib = attr = attrib
5797
dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;)

0 commit comments

Comments
 (0)