From 08bbaabaca29a7fd245f78736cc76f83082bf53b Mon Sep 17 00:00:00 2001 From: David Euresti Date: Wed, 25 Sep 2019 21:49:15 -0700 Subject: [PATCH] Fix some typing issues (#581) * Typecheck stubs in CI and fix type errors. Fixes #578 * Add overloads to instance_of definition. Improves #576 --- src/attr/validators.pyi | 35 ++++++++++++++++++++++++++++------- tests/typing_example.py | 12 +++++++++++- tox.ini | 4 +++- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/attr/validators.pyi b/src/attr/validators.pyi index 2fd7452b2..9a22abb19 100644 --- a/src/attr/validators.pyi +++ b/src/attr/validators.pyi @@ -10,18 +10,37 @@ from typing import ( Iterable, Mapping, Callable, + Match, + AnyStr, + overload, ) from . import _ValidatorType _T = TypeVar("_T") -_I = TypeVar("_I", bound=Iterable[_T]) +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_I = TypeVar("_I", bound=Iterable) _K = TypeVar("_K") _V = TypeVar("_V") -_M = TypeVar("_V", bound=Mapping[_K, _V]) +_M = TypeVar("_M", bound=Mapping) +# To be more precise on instance_of use some overloads. +# If there are more than 3 items in the tuple then we fall back to Any +@overload +def instance_of(type: Type[_T]) -> _ValidatorType[_T]: ... +@overload +def instance_of(type: Tuple[Type[_T]]) -> _ValidatorType[_T]: ... +@overload def instance_of( - type: Union[Tuple[Type[_T], ...], Type[_T]] -) -> _ValidatorType[_T]: ... + type: Tuple[Type[_T1], Type[_T2]] +) -> _ValidatorType[Union[_T1, _T2]]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2], Type[_T3]] +) -> _ValidatorType[Union[_T1, _T2, _T3]]: ... +@overload +def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ... def provides(interface: Any) -> _ValidatorType[Any]: ... def optional( validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]] @@ -29,10 +48,12 @@ def optional( def in_(options: Container[_T]) -> _ValidatorType[_T]: ... def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... def matches_re( - regex: str, + regex: AnyStr, flags: int = ..., - func: Optional[Callable[[str, str, int], ...]] = ..., -): ... + func: Optional[ + Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]] + ] = ..., +) -> _ValidatorType[AnyStr]: ... def deep_iterable( member_validator: _ValidatorType[_T], iterable_validator: Optional[_ValidatorType[_I]] = ..., diff --git a/tests/typing_example.py b/tests/typing_example.py index 4df109da0..c2690e239 100644 --- a/tests/typing_example.py +++ b/tests/typing_example.py @@ -1,6 +1,6 @@ import re -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List, Tuple, Union import attr @@ -158,6 +158,16 @@ class Validated: validator=attr.validators.matches_re(r"foo", flags=42, func=re.search) ) + # Test different forms of instance_of + g: int = attr.ib(validator=attr.validators.instance_of(int)) + h: int = attr.ib(validator=attr.validators.instance_of((int,))) + j: Union[int, str] = attr.ib( + validator=attr.validators.instance_of((int, str)) + ) + k: Union[int, str, C] = attr.ib( + validator=attr.validators.instance_of((int, C, str)) + ) + # Custom repr() @attr.s diff --git a/tox.ini b/tox.ini index 1a3b5ec2b..77ebe245a 100644 --- a/tox.ini +++ b/tox.ini @@ -92,4 +92,6 @@ commands = towncrier --draft [testenv:typing] basepython = python3.7 deps = mypy -commands = mypy tests/typing_example.py +commands = + mypy src/attr/__init__.pyi src/attr/converters.pyi src/attr/exceptions.pyi src/attr/filters.pyi src/attr/validators.pyi + mypy tests/typing_example.py