Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PEP484 stubs #238

Merged
merged 74 commits into from
Jul 12, 2018
Merged
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
e3d43a9
Add PEP484 stubs
chadrik Aug 27, 2017
a725e37
Deploy .pyi stubs alongside .py files.
chadrik Aug 30, 2017
c60d2f1
Add support for the new type argument.
chadrik Sep 25, 2017
642ddb5
Add tests for stubs and address a few issues.
chadrik Sep 28, 2017
0df5b9a
Improve declaration of private vs public objects in stubs
chadrik Nov 3, 2017
d0b9253
More stub tests
chadrik Nov 3, 2017
9030de3
Separate the stub tests into their own tox env
chadrik Nov 4, 2017
cedf2c8
Update the manifest with stub files
chadrik Nov 4, 2017
a822cee
Remove mypy from the dev requirements
chadrik Nov 4, 2017
d58379e
Allow _CountingAttr to be instantiated, but not Attribute.
chadrik Nov 4, 2017
ec7d29c
Incorporate defaults into attr.ib typing
chadrik Nov 4, 2017
8cb8000
Fix a bug with validators.and_
chadrik Nov 4, 2017
ebd6c17
Add more tests
chadrik Nov 4, 2017
19857ec
Remove _CountingAttr from public interface
chadrik Nov 7, 2017
7b953d2
Lie about return type of Factory
chadrik Nov 8, 2017
102e21b
Merge branch 'master' into pyi_stubs
wsanchez Nov 8, 2017
b68657f
Add tox stubs env to travis
chadrik Nov 8, 2017
4a2d589
used the wrong comment character in mypy tests
chadrik Nov 8, 2017
f254513
Improve overloads using PyCharm order-based approach
chadrik Nov 11, 2017
121b742
Remove features not yet working in mypy. Document remaining issues.
chadrik Dec 14, 2017
c42bbd2
Test stubs against euresti fork of mypy with attrs plugin
chadrik Dec 21, 2017
db25517
Add some types and TypeVars to some types. Make tests pass
euresti Dec 22, 2017
ff72441
Suppress warnings about named attribute access from fields()
chadrik Dec 22, 2017
4fe065b
Add WIP mypy-doctest plugin
chadrik Dec 22, 2017
762d976
Deal with a few remaining type issues in the docs
chadrik Dec 22, 2017
00b8e3e
Merge branch 'master' into pyi_stubs
chadrik Dec 22, 2017
ecc3484
sphinx doctest: don't turn warnings into errors.
chadrik Dec 29, 2017
2cd9d53
Update "type: ignore" comments to reflect issues fixed in mypy plugin
chadrik Dec 29, 2017
5bf1564
doctest2: improve output formatting
chadrik Dec 29, 2017
db2d269
Update manifest
chadrik Dec 29, 2017
850a36d
static tests: use inline error declarations
chadrik Dec 29, 2017
7e03c2f
More tests
chadrik Dec 29, 2017
2293070
Tests passing (with notes about remaining issues)
chadrik Dec 31, 2017
5ba0539
Attempt to get latest plugin from euresti working
chadrik Dec 31, 2017
70e108f
Issues fixed.
chadrik Dec 31, 2017
c69ff42
Deal with a PyCharm bug
chadrik Dec 31, 2017
c9a975a
Minor test improvements
chadrik Dec 31, 2017
a900a74
Make tests prettier
chadrik Jan 1, 2018
cc4bbdc
Use 2 decorators instead of 3
euresti Jan 1, 2018
7fcd7c8
doctest2: add support for skipping mypy tests
chadrik Jan 1, 2018
7d97c44
Merge remote-tracking branch 'euresti/pyi_stubs' into pyi_stubs
chadrik Jan 1, 2018
a430345
Add tests for inheritance, eq, and cmp
chadrik Jan 3, 2018
f0900aa
Add fixmes and todos
chadrik Jan 3, 2018
6e1c818
Merge branch 'master' into pyi_stubs
chadrik Jan 3, 2018
7344ac4
Rename convert to converter
chadrik Jan 3, 2018
11b47a0
Attribute.validator is always a single validator
chadrik Jan 3, 2018
d7e5783
Conform stubs to typeshed coding style
chadrik Jan 3, 2018
b745de6
backport style fixes from typeshed
chadrik Feb 2, 2018
4863502
Add test cases to cover forward references and Any
chadrik Feb 2, 2018
84d3eb4
Add fixes for forward references and Any
chadrik Feb 2, 2018
0db6009
Address typeshed review notes
chadrik Feb 2, 2018
7087828
Use Sequence instead of List/Tuple for validator arg
chadrik Feb 2, 2018
201ad63
backports changes from typeshed #1914
chadrik Feb 25, 2018
e4461f4
backport changes from typeshed #1933
chadrik Mar 1, 2018
3ce2503
Merge with master
chadrik Mar 11, 2018
3fc633c
Prevent mypy tests from getting picked up
chadrik Mar 11, 2018
175ad70
Merge branch 'master' into pyi_stubs
hynek Apr 12, 2018
0ab6d5c
make our doctest extension compatible with latest sphinx
chadrik Apr 14, 2018
c95d5ae
Adjustments to the tests
chadrik Apr 14, 2018
29c60d2
Merge remote-tracking branch 'upstream/master' into pyi_stubs
chadrik Apr 14, 2018
0ddec13
Merge remote-tracking branch 'origin/pyi_stubs' into pyi_stubs
chadrik Apr 14, 2018
f33712d
Fix flake and manifest tests (hopefully)
chadrik Apr 14, 2018
4ac5590
Fix tests on pypy3 (hopefully)
chadrik Apr 16, 2018
2c77bca
Merge remote-tracking branch 'upstream/master' into pyi_stubs
chadrik May 26, 2018
e13612c
Update stubs from typeshed
chadrik Jul 7, 2018
8c9ca24
Merge branch 'master' into pyi_stubs
chadrik Jul 7, 2018
af4dbcc
Make PEP 561-compliant
chadrik Jul 9, 2018
b922698
minor cleanup
chadrik Jul 9, 2018
989cd88
Consolidate stub support files into stub directory
chadrik Jul 10, 2018
875d2ac
Get tests passing
chadrik Jul 10, 2018
60dd489
Revert stub test additions
chadrik Jul 10, 2018
2b19c36
get pre-commit passing
chadrik Jul 11, 2018
88fcd12
Address review feedback
chadrik Jul 11, 2018
85862cb
Move typing test up in tox envlist
chadrik Jul 11, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
.cache
.coverage*
.hypothesis
.mypy_cache
.pytest_cache
.tox
build
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -4,6 +4,9 @@ repos:
hooks:
- id: black
language_version: python3.6
# override until resolved: https://github.com/ambv/black/issues/402
files: \.pyi?$
types: []

- repo: https://github.com/asottile/seed-isort-config
rev: v1.0.1
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -46,6 +46,8 @@ matrix:
env: TOXENV=readme
- python: "3.6"
env: TOXENV=changelog
- python: "3.6"
env: TOXENV=typing

allow_failures:
- python: "3.6-dev"
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -3,6 +3,10 @@ include LICENSE *.rst *.toml .readthedocs.yml .pre-commit-config.yaml
# Don't package GitHub-specific files.
exclude .github/*.md .travis.yml codecov.yml

# Stubs
include src/attr/py.typed
recursive-include src *.pyi

# Tests
include tox.ini .coveragerc conftest.py
recursive-include tests *.py
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -115,4 +115,5 @@ def find_meta(meta):
classifiers=CLASSIFIERS,
install_requires=INSTALL_REQUIRES,
extras_require=EXTRAS_REQUIRE,
include_package_data=True,
)
240 changes: 240 additions & 0 deletions src/attr/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
from typing import (

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

Any,
Callable,
Dict,
Generic,
List,
Optional,
Sequence,
Mapping,
Tuple,
Type,
TypeVar,
Union,
overload,
)

# `import X as X` is required to make these public
from . import exceptions as exceptions
from . import filters as filters
from . import converters as converters
from . import validators as validators

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

_ValidatorType = Callable[[Any, Attribute, _T], Any]
_ConverterType = Callable[[Any], _T]
_FilterType = Callable[[Attribute, Any], bool]
# FIXME: in reality, if multiple validators are passed they must be in a list or tuple,
# but those are invariant and so would prevent subtypes of _ValidatorType from working
# when passed in a list or tuple.
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]

# _make --

NOTHING: object

# NOTE: Factory lies about its return type to make this possible: `x: List[int] = Factory(list)`
# Work around mypy issue #4554 in the common case by using an overload.
@overload
def Factory(factory: Callable[[], _T]) -> _T: ...
@overload
def Factory(
factory: Union[Callable[[Any], _T], Callable[[], _T]],
takes_self: bool = ...,
) -> _T: ...

class Attribute(Generic[_T]):
name: str
default: Optional[_T]
validator: Optional[_ValidatorType[_T]]
repr: bool
cmp: bool
hash: Optional[bool]
init: bool
converter: Optional[_ConverterType[_T]]
metadata: Dict[Any, Any]
type: Optional[Type[_T]]
def __lt__(self, x: Attribute) -> bool: ...
def __le__(self, x: Attribute) -> bool: ...
def __gt__(self, x: Attribute) -> bool: ...
def __ge__(self, x: Attribute) -> bool: ...

# NOTE: We had several choices for the annotation to use for type arg:
# 1) Type[_T]
# - Pros: works in PyCharm without plugin support
# - Cons: produces less informative error in the case of conflicting TypeVars
# e.g. `attr.ib(default='bad', type=int)`
# 2) Callable[..., _T]
# - Pros: more informative errors than #1
# - Cons: validator tests results in confusing error.
# e.g. `attr.ib(type=int, validator=validate_str)`
# 3) type (and do all of the work in the mypy plugin)
# - Pros: in mypy, the behavior of type argument is exactly the same as with
# annotations.
# - Cons: completely disables type inspections in PyCharm when using the
# type arg.
# We chose option #1 until either PyCharm adds support for attrs, or python 2
# reaches EOL.

# `attr` lies about its return type to make the following possible:
# attr() -> Any
# attr(8) -> int
# attr(validator=<some callable>) -> Whatever the callable expects.
# This makes this type of assignments possible:
# x: int = attr(8)
#
# This form catches explicit None or no default but with no other arguments returns Any.
@overload
def attrib(
default: None = ...,
validator: None = ...,
repr: bool = ...,
cmp: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
convert: None = ...,
metadata: Optional[Mapping[Any, Any]] = ...,
type: None = ...,
converter: None = ...,
factory: None = ...,
) -> Any: ...

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

# This form catches an explicit default argument.
@overload
def attrib(
default: _T,
validator: Optional[_ValidatorArgType[_T]] = ...,
repr: bool = ...,
cmp: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
convert: Optional[_ConverterType[_T]] = ...,
metadata: Optional[Mapping[Any, Any]] = ...,
type: Optional[Type[_T]] = ...,
converter: Optional[_ConverterType[_T]] = ...,
factory: Optional[Callable[[], _T]] = ...,
) -> _T: ...

# This form covers type=non-Type: e.g. forward references (str), Any
@overload
def attrib(
default: Optional[_T] = ...,
validator: Optional[_ValidatorArgType[_T]] = ...,
repr: bool = ...,
cmp: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
convert: Optional[_ConverterType[_T]] = ...,
metadata: Optional[Mapping[Any, Any]] = ...,
type: object = ...,
converter: Optional[_ConverterType[_T]] = ...,
factory: Optional[Callable[[], _T]] = ...,
) -> Any: ...
@overload
def attrs(
maybe_cls: _C,
these: Optional[Dict[str, Any]] = ...,
repr_ns: Optional[str] = ...,
repr: bool = ...,
cmp: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
slots: bool = ...,
frozen: bool = ...,
str: bool = ...,
auto_attribs: bool = ...,
) -> _C: ...
@overload
def attrs(
maybe_cls: None = ...,
these: Optional[Dict[str, Any]] = ...,
repr_ns: Optional[str] = ...,
repr: bool = ...,
cmp: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
slots: bool = ...,
frozen: bool = ...,
str: bool = ...,
auto_attribs: bool = ...,
) -> Callable[[_C], _C]: ...

# TODO: add support for returning NamedTuple from the mypy plugin
class _Fields(Tuple[Attribute, ...]):
def __getattr__(self, name: str) -> Attribute: ...

def fields(cls: type) -> _Fields: ...
def fields_dict(cls: type) -> Dict[str, Attribute]: ...
def validate(inst: Any) -> None: ...

# TODO: add support for returning a proper attrs class from the mypy plugin
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', [attr.ib()])` is valid
def make_class(
name: str,
attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
bases: Tuple[type, ...] = ...,
repr_ns: Optional[str] = ...,
repr: bool = ...,
cmp: bool = ...,
hash: Optional[bool] = ...,
init: bool = ...,
slots: bool = ...,
frozen: bool = ...,
str: bool = ...,
auto_attribs: bool = ...,
) -> type: ...

# _funcs --

# TODO: add support for returning TypedDict from the mypy plugin
# FIXME: asdict/astuple do not honor their factory args. waiting on one of these:
# https://github.com/python/mypy/issues/4236
# https://github.com/python/typing/issues/253
def asdict(
inst: Any,
recurse: bool = ...,
filter: Optional[_FilterType] = ...,
dict_factory: Type[Mapping[Any, Any]] = ...,
retain_collection_types: bool = ...,
) -> Dict[str, Any]: ...

# TODO: add support for returning NamedTuple from the mypy plugin
def astuple(
inst: Any,
recurse: bool = ...,
filter: Optional[_FilterType] = ...,
tuple_factory: Type[Sequence] = ...,
retain_collection_types: bool = ...,
) -> Tuple[Any, ...]: ...
def has(cls: type) -> bool: ...
def assoc(inst: _T, **changes: Any) -> _T: ...
def evolve(inst: _T, **changes: Any) -> _T: ...

# _config --

def set_run_validators(run: bool) -> None: ...
def get_run_validators() -> bool: ...

# aliases --

s = attributes = attrs
ib = attr = attrib
dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;)
8 changes: 8 additions & 0 deletions src/attr/converters.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from typing import TypeVar, Optional
from . import _ConverterType

_T = TypeVar("_T")

def optional(
converter: _ConverterType[_T]
) -> _ConverterType[Optional[_T]]: ...
7 changes: 7 additions & 0 deletions src/attr/exceptions.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class FrozenInstanceError(AttributeError):
msg: str = ...

class AttrsAttributeNotFoundError(ValueError): ...
class NotAnAttrsClassError(ValueError): ...
class DefaultAlreadySetError(RuntimeError): ...
class UnannotatedAttributeError(RuntimeError): ...
5 changes: 5 additions & 0 deletions src/attr/filters.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from typing import Union
from . import Attribute, _FilterType

def include(*what: Union[type, Attribute]) -> _FilterType: ...
def exclude(*what: Union[type, Attribute]) -> _FilterType: ...
Empty file added src/attr/py.typed
Empty file.
14 changes: 14 additions & 0 deletions src/attr/validators.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from typing import Container, List, Union, TypeVar, Type, Any, Optional, Tuple
from . import _ValidatorType

_T = TypeVar("_T")

def instance_of(
type: Union[Tuple[Type[_T], ...], Type[_T]]
) -> _ValidatorType[_T]: ...
def provides(interface: Any) -> _ValidatorType[Any]: ...
def optional(
validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]]
) -> _ValidatorType[Optional[_T]]: ...
def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
80 changes: 80 additions & 0 deletions tests/typing_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from typing import Any, List

import attr


# Typing via "type" Argument ---


@attr.s
class C:
a = attr.ib(type=int)


c = C(1)
C(a=1)


@attr.s
class D:
x = attr.ib(type=List[int])


@attr.s
class E:
y = attr.ib(type="List[int]")


@attr.s
class F:
z = attr.ib(type=Any)


# Typing via Annotations ---


@attr.s
class CC:
a: int = attr.ib()


cc = CC(1)
CC(a=1)


@attr.s
class DD:
x: List[int] = attr.ib()


@attr.s
class EE:
y: "List[int]" = attr.ib()


@attr.s
class FF:
z: Any = attr.ib()


# Inheritance --


@attr.s
class GG(DD):
y: str = attr.ib()


GG(x=[1], y="foo")


@attr.s
class HH(DD, EE):
z: float = attr.ib()


HH(x=[1], y=[], z=1.1)


# same class
c == cc
7 changes: 6 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = pre-commit,lint,py27,py34,py35,py36,py37,pypy,pypy3,manifest,docs,readme,changelog,coverage-report
envlist = pre-commit,typing,lint,py27,py34,py35,py36,py37,pypy,pypy3,manifest,docs,readme,changelog,coverage-report


[testenv]
@@ -86,3 +86,8 @@ basepython = python3.6
deps = towncrier
skip_install = true
commands = towncrier --draft

[testenv:typing]
basepython = python3.6
deps = mypy
commands = mypy tests/typing_example.py