Skip to content

Commit 5ed5051

Browse files
authored
Merge branch 'master' into types-docs
2 parents ca6cba5 + de104e0 commit 5ed5051

13 files changed

+371
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
.cache
44
.coverage*
55
.hypothesis
6+
.mypy_cache
67
.pytest_cache
78
.tox
89
build

.pre-commit-config.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ repos:
44
hooks:
55
- id: black
66
language_version: python3.6
7+
# override until resolved: https://github.com/ambv/black/issues/402
8+
files: \.pyi?$
9+
types: []
710

811
- repo: https://github.com/asottile/seed-isort-config
912
rev: v1.0.1

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ matrix:
4646
env: TOXENV=readme
4747
- python: "3.6"
4848
env: TOXENV=changelog
49+
- python: "3.6"
50+
env: TOXENV=typing
4951

5052
allow_failures:
5153
- python: "3.6-dev"

MANIFEST.in

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ include LICENSE *.rst *.toml .readthedocs.yml .pre-commit-config.yaml
33
# Don't package GitHub-specific files.
44
exclude .github/*.md .travis.yml codecov.yml
55

6+
# Stubs
7+
include src/attr/py.typed
8+
recursive-include src *.pyi
9+
610
# Tests
711
include tox.ini .coveragerc conftest.py
812
recursive-include tests *.py

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,5 @@ def find_meta(meta):
115115
classifiers=CLASSIFIERS,
116116
install_requires=INSTALL_REQUIRES,
117117
extras_require=EXTRAS_REQUIRE,
118+
include_package_data=True,
118119
)

src/attr/__init__.pyi

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
from typing import (
2+
Any,
3+
Callable,
4+
Dict,
5+
Generic,
6+
List,
7+
Optional,
8+
Sequence,
9+
Mapping,
10+
Tuple,
11+
Type,
12+
TypeVar,
13+
Union,
14+
overload,
15+
)
16+
17+
# `import X as X` is required to make these public
18+
from . import exceptions as exceptions
19+
from . import filters as filters
20+
from . import converters as converters
21+
from . import validators as validators
22+
23+
_T = TypeVar("_T")
24+
_C = TypeVar("_C", bound=type)
25+
26+
_ValidatorType = Callable[[Any, Attribute, _T], Any]
27+
_ConverterType = Callable[[Any], _T]
28+
_FilterType = Callable[[Attribute, Any], bool]
29+
# FIXME: in reality, if multiple validators are passed they must be in a list or tuple,
30+
# but those are invariant and so would prevent subtypes of _ValidatorType from working
31+
# when passed in a list or tuple.
32+
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
33+
34+
# _make --
35+
36+
NOTHING: object
37+
38+
# NOTE: Factory lies about its return type to make this possible: `x: List[int] = Factory(list)`
39+
# Work around mypy issue #4554 in the common case by using an overload.
40+
@overload
41+
def Factory(factory: Callable[[], _T]) -> _T: ...
42+
@overload
43+
def Factory(
44+
factory: Union[Callable[[Any], _T], Callable[[], _T]],
45+
takes_self: bool = ...,
46+
) -> _T: ...
47+
48+
class Attribute(Generic[_T]):
49+
name: str
50+
default: Optional[_T]
51+
validator: Optional[_ValidatorType[_T]]
52+
repr: bool
53+
cmp: bool
54+
hash: Optional[bool]
55+
init: bool
56+
converter: Optional[_ConverterType[_T]]
57+
metadata: Dict[Any, Any]
58+
type: Optional[Type[_T]]
59+
def __lt__(self, x: Attribute) -> bool: ...
60+
def __le__(self, x: Attribute) -> bool: ...
61+
def __gt__(self, x: Attribute) -> bool: ...
62+
def __ge__(self, x: Attribute) -> bool: ...
63+
64+
# NOTE: We had several choices for the annotation to use for type arg:
65+
# 1) Type[_T]
66+
# - Pros: works in PyCharm without plugin support
67+
# - Cons: produces less informative error in the case of conflicting TypeVars
68+
# e.g. `attr.ib(default='bad', type=int)`
69+
# 2) Callable[..., _T]
70+
# - Pros: more informative errors than #1
71+
# - Cons: validator tests results in confusing error.
72+
# e.g. `attr.ib(type=int, validator=validate_str)`
73+
# 3) type (and do all of the work in the mypy plugin)
74+
# - Pros: in mypy, the behavior of type argument is exactly the same as with
75+
# annotations.
76+
# - Cons: completely disables type inspections in PyCharm when using the
77+
# type arg.
78+
# We chose option #1 until either PyCharm adds support for attrs, or python 2
79+
# reaches EOL.
80+
81+
# `attr` lies about its return type to make the following possible:
82+
# attr() -> Any
83+
# attr(8) -> int
84+
# attr(validator=<some callable>) -> Whatever the callable expects.
85+
# This makes this type of assignments possible:
86+
# x: int = attr(8)
87+
#
88+
# This form catches explicit None or no default but with no other arguments returns Any.
89+
@overload
90+
def attrib(
91+
default: None = ...,
92+
validator: None = ...,
93+
repr: bool = ...,
94+
cmp: bool = ...,
95+
hash: Optional[bool] = ...,
96+
init: bool = ...,
97+
convert: None = ...,
98+
metadata: Optional[Mapping[Any, Any]] = ...,
99+
type: None = ...,
100+
converter: None = ...,
101+
factory: None = ...,
102+
) -> Any: ...
103+
104+
# This form catches an explicit None or no default and infers the type from the other arguments.
105+
@overload
106+
def attrib(
107+
default: None = ...,
108+
validator: Optional[_ValidatorArgType[_T]] = ...,
109+
repr: bool = ...,
110+
cmp: bool = ...,
111+
hash: Optional[bool] = ...,
112+
init: bool = ...,
113+
convert: Optional[_ConverterType[_T]] = ...,
114+
metadata: Optional[Mapping[Any, Any]] = ...,
115+
type: Optional[Type[_T]] = ...,
116+
converter: Optional[_ConverterType[_T]] = ...,
117+
factory: Optional[Callable[[], _T]] = ...,
118+
) -> _T: ...
119+
120+
# This form catches an explicit default argument.
121+
@overload
122+
def attrib(
123+
default: _T,
124+
validator: Optional[_ValidatorArgType[_T]] = ...,
125+
repr: bool = ...,
126+
cmp: bool = ...,
127+
hash: Optional[bool] = ...,
128+
init: bool = ...,
129+
convert: Optional[_ConverterType[_T]] = ...,
130+
metadata: Optional[Mapping[Any, Any]] = ...,
131+
type: Optional[Type[_T]] = ...,
132+
converter: Optional[_ConverterType[_T]] = ...,
133+
factory: Optional[Callable[[], _T]] = ...,
134+
) -> _T: ...
135+
136+
# This form covers type=non-Type: e.g. forward references (str), Any
137+
@overload
138+
def attrib(
139+
default: Optional[_T] = ...,
140+
validator: Optional[_ValidatorArgType[_T]] = ...,
141+
repr: bool = ...,
142+
cmp: bool = ...,
143+
hash: Optional[bool] = ...,
144+
init: bool = ...,
145+
convert: Optional[_ConverterType[_T]] = ...,
146+
metadata: Optional[Mapping[Any, Any]] = ...,
147+
type: object = ...,
148+
converter: Optional[_ConverterType[_T]] = ...,
149+
factory: Optional[Callable[[], _T]] = ...,
150+
) -> Any: ...
151+
@overload
152+
def attrs(
153+
maybe_cls: _C,
154+
these: Optional[Dict[str, Any]] = ...,
155+
repr_ns: Optional[str] = ...,
156+
repr: bool = ...,
157+
cmp: bool = ...,
158+
hash: Optional[bool] = ...,
159+
init: bool = ...,
160+
slots: bool = ...,
161+
frozen: bool = ...,
162+
str: bool = ...,
163+
auto_attribs: bool = ...,
164+
) -> _C: ...
165+
@overload
166+
def attrs(
167+
maybe_cls: None = ...,
168+
these: Optional[Dict[str, Any]] = ...,
169+
repr_ns: Optional[str] = ...,
170+
repr: bool = ...,
171+
cmp: bool = ...,
172+
hash: Optional[bool] = ...,
173+
init: bool = ...,
174+
slots: bool = ...,
175+
frozen: bool = ...,
176+
str: bool = ...,
177+
auto_attribs: bool = ...,
178+
) -> Callable[[_C], _C]: ...
179+
180+
# TODO: add support for returning NamedTuple from the mypy plugin
181+
class _Fields(Tuple[Attribute, ...]):
182+
def __getattr__(self, name: str) -> Attribute: ...
183+
184+
def fields(cls: type) -> _Fields: ...
185+
def fields_dict(cls: type) -> Dict[str, Attribute]: ...
186+
def validate(inst: Any) -> None: ...
187+
188+
# TODO: add support for returning a proper attrs class from the mypy plugin
189+
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', [attr.ib()])` is valid
190+
def make_class(
191+
name: str,
192+
attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
193+
bases: Tuple[type, ...] = ...,
194+
repr_ns: Optional[str] = ...,
195+
repr: bool = ...,
196+
cmp: bool = ...,
197+
hash: Optional[bool] = ...,
198+
init: bool = ...,
199+
slots: bool = ...,
200+
frozen: bool = ...,
201+
str: bool = ...,
202+
auto_attribs: bool = ...,
203+
) -> type: ...
204+
205+
# _funcs --
206+
207+
# TODO: add support for returning TypedDict from the mypy plugin
208+
# FIXME: asdict/astuple do not honor their factory args. waiting on one of these:
209+
# https://github.com/python/mypy/issues/4236
210+
# https://github.com/python/typing/issues/253
211+
def asdict(
212+
inst: Any,
213+
recurse: bool = ...,
214+
filter: Optional[_FilterType] = ...,
215+
dict_factory: Type[Mapping[Any, Any]] = ...,
216+
retain_collection_types: bool = ...,
217+
) -> Dict[str, Any]: ...
218+
219+
# TODO: add support for returning NamedTuple from the mypy plugin
220+
def astuple(
221+
inst: Any,
222+
recurse: bool = ...,
223+
filter: Optional[_FilterType] = ...,
224+
tuple_factory: Type[Sequence] = ...,
225+
retain_collection_types: bool = ...,
226+
) -> Tuple[Any, ...]: ...
227+
def has(cls: type) -> bool: ...
228+
def assoc(inst: _T, **changes: Any) -> _T: ...
229+
def evolve(inst: _T, **changes: Any) -> _T: ...
230+
231+
# _config --
232+
233+
def set_run_validators(run: bool) -> None: ...
234+
def get_run_validators() -> bool: ...
235+
236+
# aliases --
237+
238+
s = attributes = attrs
239+
ib = attr = attrib
240+
dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;)

src/attr/converters.pyi

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from typing import TypeVar, Optional
2+
from . import _ConverterType
3+
4+
_T = TypeVar("_T")
5+
6+
def optional(
7+
converter: _ConverterType[_T]
8+
) -> _ConverterType[Optional[_T]]: ...

src/attr/exceptions.pyi

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class FrozenInstanceError(AttributeError):
2+
msg: str = ...
3+
4+
class AttrsAttributeNotFoundError(ValueError): ...
5+
class NotAnAttrsClassError(ValueError): ...
6+
class DefaultAlreadySetError(RuntimeError): ...
7+
class UnannotatedAttributeError(RuntimeError): ...

src/attr/filters.pyi

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from typing import Union
2+
from . import Attribute, _FilterType
3+
4+
def include(*what: Union[type, Attribute]) -> _FilterType: ...
5+
def exclude(*what: Union[type, Attribute]) -> _FilterType: ...

src/attr/py.typed

Whitespace-only changes.

src/attr/validators.pyi

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from typing import Container, List, Union, TypeVar, Type, Any, Optional, Tuple
2+
from . import _ValidatorType
3+
4+
_T = TypeVar("_T")
5+
6+
def instance_of(
7+
type: Union[Tuple[Type[_T], ...], Type[_T]]
8+
) -> _ValidatorType[_T]: ...
9+
def provides(interface: Any) -> _ValidatorType[Any]: ...
10+
def optional(
11+
validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]]
12+
) -> _ValidatorType[Optional[_T]]: ...
13+
def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
14+
def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...

0 commit comments

Comments
 (0)