From 006397f857da4b189c68615bb2e2827bbf2a911b Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Thu, 17 May 2018 09:45:33 -0400 Subject: [PATCH] Ensures overloads are ordered from narrow to broad (#2138) This commit reorders any overloads where the first overload was "shadowing" the second, preventing it from ever being matched by type checkers that work by selecting the first matching overload alternative. For example, the first overload alternative below is strictly broader then the second, preventing it from ever being selected: class Parent: pass class Child(Parent): pass @overload def foo(x: *int) -> Parent: ... @overload def foo(x: int, y: int) -> Child: ... The correct thing to do is to either delete the second overload or rearrange them to look like this: @overload def foo(x: int, y: int) -> Child: ... @overload def foo(x: *int) -> Parent: ... Rationale: I'm currently [working on a proposal][0] that would amend PEP 484 to (a) mandate type checkers check overloads in order and (b) prohibit overloads where an earlier alternative completely shadows a later one. [0]: https://github.com/python/typing/issues/253#issuecomment-389262904 This would prohibit overloads that look like the example below, where the first alternative completely shadows the second. I figured it would be a good idea to make these changes ahead of time: if my proposal is accepted, it'd make the transition smoother. If not, this is hopefully a relatively harmless change. Note: I think some of these overloads could be simplified (e.g. `reversed(...)`), but I mostly stuck with rearranging them in case I was wrong. The only overload I actually changed was `hmac.compare_digest` -- I believe the Python 2 version actually accepts unicode. --- stdlib/2/__builtin__.pyi | 4 +- stdlib/2/builtins.pyi | 4 +- stdlib/2/os/__init__.pyi | 4 +- stdlib/2/os/path.pyi | 8 +-- stdlib/2and3/hmac.pyi | 8 ++- stdlib/2and3/sysconfig.pyi | 4 +- stdlib/3/builtins.pyi | 8 +-- stdlib/3/typing.pyi | 4 +- stdlib/3/urllib/parse.pyi | 8 +-- third_party/2and3/attr/__init__.pyi | 82 ++++++++++++++--------------- 10 files changed, 66 insertions(+), 68 deletions(-) diff --git a/stdlib/2/__builtin__.pyi b/stdlib/2/__builtin__.pyi index 6a390b12ffe3..00756191be14 100644 --- a/stdlib/2/__builtin__.pyi +++ b/stdlib/2/__builtin__.pyi @@ -863,9 +863,9 @@ def reduce(function: Callable[[_T, _T], _T], iterable: Iterable[_T]) -> _T: ... def reload(module: Any) -> Any: ... @overload -def reversed(object: Reversible[_T]) -> Iterator[_T]: ... -@overload def reversed(object: Sequence[_T]) -> Iterator[_T]: ... +@overload +def reversed(object: Reversible[_T]) -> Iterator[_T]: ... def repr(o: object) -> str: ... @overload def round(number: float) -> float: ... diff --git a/stdlib/2/builtins.pyi b/stdlib/2/builtins.pyi index 6a390b12ffe3..00756191be14 100644 --- a/stdlib/2/builtins.pyi +++ b/stdlib/2/builtins.pyi @@ -863,9 +863,9 @@ def reduce(function: Callable[[_T, _T], _T], iterable: Iterable[_T]) -> _T: ... def reload(module: Any) -> Any: ... @overload -def reversed(object: Reversible[_T]) -> Iterator[_T]: ... -@overload def reversed(object: Sequence[_T]) -> Iterator[_T]: ... +@overload +def reversed(object: Reversible[_T]) -> Iterator[_T]: ... def repr(o: object) -> str: ... @overload def round(number: float) -> float: ... diff --git a/stdlib/2/os/__init__.pyi b/stdlib/2/os/__init__.pyi index d40532b116dd..2fa45e3602e5 100644 --- a/stdlib/2/os/__init__.pyi +++ b/stdlib/2/os/__init__.pyi @@ -219,9 +219,9 @@ def renames(old: _PathType, new: _PathType) -> None: ... def rmdir(path: _PathType) -> None: ... def stat(path: _PathType) -> Any: ... @overload -def stat_float_times(newvalue: bool = ...) -> None: ... -@overload def stat_float_times() -> bool: ... +@overload +def stat_float_times(newvalue: bool) -> None: ... def statvfs(path: _PathType) -> _StatVFS: ... # Unix only def symlink(source: _PathType, link_name: _PathType) -> None: ... def unlink(path: _PathType) -> None: ... diff --git a/stdlib/2/os/path.pyi b/stdlib/2/os/path.pyi index 2cb23811ce5f..bd32c5e6c63c 100644 --- a/stdlib/2/os/path.pyi +++ b/stdlib/2/os/path.pyi @@ -123,13 +123,13 @@ if sys.version_info < (3, 0): @overload def join(__p1: bytes, *p: bytes) -> bytes: ... @overload - def join(__p1: Text, *p: _PathType) -> Text: ... - @overload - def join(__p1: bytes, __p2: Text, *p: _PathType) -> Text: ... + def join(__p1: bytes, __p2: bytes, __p3: bytes, __p4: Text, *p: _PathType) -> Text: ... @overload def join(__p1: bytes, __p2: bytes, __p3: Text, *p: _PathType) -> Text: ... @overload - def join(__p1: bytes, __p2: bytes, __p3: bytes, __p4: Text, *p: _PathType) -> Text: ... + def join(__p1: bytes, __p2: Text, *p: _PathType) -> Text: ... + @overload + def join(__p1: Text, *p: _PathType) -> Text: ... elif sys.version_info >= (3, 6): # Mypy complains that the signatures overlap (same for relpath below), but things seem to behave correctly anyway. @overload diff --git a/stdlib/2and3/hmac.pyi b/stdlib/2and3/hmac.pyi index 830d53ecb5db..fb5029c59d5e 100644 --- a/stdlib/2and3/hmac.pyi +++ b/stdlib/2and3/hmac.pyi @@ -1,6 +1,6 @@ # Stubs for hmac -from typing import Any, Callable, Optional, Union, overload +from typing import Any, Callable, Optional, Union, overload, AnyStr from types import ModuleType import sys @@ -29,12 +29,10 @@ class HMAC: def hexdigest(self) -> str: ... def copy(self) -> HMAC: ... -@overload -def compare_digest(a: str, b: str) -> bool: ... -@overload -def compare_digest(a: bytes, b: bytes) -> bool: ... @overload def compare_digest(a: bytearray, b: bytearray) -> bool: ... +@overload +def compare_digest(a: AnyStr, b: AnyStr) -> bool: ... if sys.version_info >= (3, 7): def digest(key: _B, msg: _B, digest: str) -> bytes: ... diff --git a/stdlib/2and3/sysconfig.pyi b/stdlib/2and3/sysconfig.pyi index 09fd94690aa7..844caa851203 100644 --- a/stdlib/2and3/sysconfig.pyi +++ b/stdlib/2and3/sysconfig.pyi @@ -2,10 +2,10 @@ from typing import overload, Any, Dict, IO, List, Optional, Tuple, Union -@overload -def get_config_vars(*args: str) -> List[Any]: ... @overload def get_config_vars() -> Dict[str, Any]: ... +@overload +def get_config_vars(*args: str) -> List[Any]: ... def get_config_var(name: str) -> Optional[str]: ... def get_scheme_names() -> Tuple[str, ...]: ... def get_path_names() -> Tuple[str, ...]: ... diff --git a/stdlib/3/builtins.pyi b/stdlib/3/builtins.pyi index 51337b9679bd..d73f4011205e 100644 --- a/stdlib/3/builtins.pyi +++ b/stdlib/3/builtins.pyi @@ -816,9 +816,9 @@ def eval(source: Union[str, bytes, CodeType], globals: Optional[Dict[str, Any]] def exec(object: Union[str, bytes, CodeType], globals: Optional[Dict[str, Any]] = ..., locals: Optional[Mapping[str, Any]] = ...) -> Any: ... def exit(code: Any = ...) -> NoReturn: ... @overload -def filter(function: Callable[[_T], Any], iterable: Iterable[_T]) -> Iterator[_T]: ... -@overload def filter(function: None, iterable: Iterable[Optional[_T]]) -> Iterator[_T]: ... +@overload +def filter(function: Callable[[_T], Any], iterable: Iterable[_T]) -> Iterator[_T]: ... def format(o: object, format_spec: str = ...) -> str: ... def getattr(o: Any, name: str, default: Any = ...) -> Any: ... def globals() -> Dict[str, Any]: ... @@ -908,9 +908,9 @@ def pow(x: float, y: float) -> float: ... def pow(x: float, y: float, z: float) -> float: ... def quit(code: Optional[int] = ...) -> None: ... @overload -def reversed(object: Reversible[_T]) -> Iterator[_T]: ... -@overload def reversed(object: Sequence[_T]) -> Iterator[_T]: ... +@overload +def reversed(object: Reversible[_T]) -> Iterator[_T]: ... def repr(o: object) -> str: ... @overload def round(number: float) -> int: ... diff --git a/stdlib/3/typing.pyi b/stdlib/3/typing.pyi index 1871d6bcd181..ac350a957bb4 100644 --- a/stdlib/3/typing.pyi +++ b/stdlib/3/typing.pyi @@ -434,10 +434,10 @@ class BinaryIO(IO[bytes]): # TODO peek? @overload @abstractmethod - def write(self, s: bytes) -> int: ... + def write(self, s: bytearray) -> int: ... @overload @abstractmethod - def write(self, s: bytearray) -> int: ... + def write(self, s: bytes) -> int: ... @abstractmethod def __enter__(self) -> BinaryIO: ... diff --git a/stdlib/3/urllib/parse.pyi b/stdlib/3/urllib/parse.pyi index c8f4c11010a5..1055848a2a57 100644 --- a/stdlib/3/urllib/parse.pyi +++ b/stdlib/3/urllib/parse.pyi @@ -137,12 +137,12 @@ def urlsplit(url: str, scheme: str = ..., allow_fragments: bool = ...) -> SplitR @overload def urlsplit(url: bytes, scheme: bytes = ..., allow_fragments: bool = ...) -> SplitResultBytes: ... -@overload -def urlunparse(components: Sequence[AnyStr]) -> AnyStr: ... @overload def urlunparse(components: Tuple[AnyStr, AnyStr, AnyStr, AnyStr, AnyStr, AnyStr]) -> AnyStr: ... - @overload -def urlunsplit(components: Sequence[AnyStr]) -> AnyStr: ... +def urlunparse(components: Sequence[AnyStr]) -> AnyStr: ... + @overload def urlunsplit(components: Tuple[AnyStr, AnyStr, AnyStr, AnyStr, AnyStr]) -> AnyStr: ... +@overload +def urlunsplit(components: Sequence[AnyStr]) -> AnyStr: ... diff --git a/third_party/2and3/attr/__init__.pyi b/third_party/2and3/attr/__init__.pyi index 9dfd06cad0e0..d713c67f6158 100644 --- a/third_party/2and3/attr/__init__.pyi +++ b/third_party/2and3/attr/__init__.pyi @@ -69,7 +69,21 @@ class Attribute(Generic[_T]): # attr(validator=) -> 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 = ..., @@ -98,20 +112,6 @@ def attrib(default: _T, converter: Optional[_ConverterType[_T]] = ..., factory: Optional[Callable[[], _T]] = ..., ) -> _T: ... -# 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 covers type=non-Type: e.g. forward references (str), Any @overload def attrib(default: Optional[_T] = ..., @@ -216,6 +216,19 @@ def get_run_validators() -> bool: ... # dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) +@overload +def ib(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: ... @overload def ib(default: None = ..., validator: Optional[_ValidatorArgType[_T]] = ..., @@ -243,19 +256,6 @@ def ib(default: _T, factory: Optional[Callable[[], _T]] = ..., ) -> _T: ... @overload -def ib(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: ... -@overload def ib(default: Optional[_T] = ..., validator: Optional[_ValidatorArgType[_T]] = ..., repr: bool = ..., @@ -270,6 +270,19 @@ def ib(default: Optional[_T] = ..., ) -> Any: ... @overload +def attr(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: ... +@overload def attr(default: None = ..., validator: Optional[_ValidatorArgType[_T]] = ..., repr: bool = ..., @@ -296,19 +309,6 @@ def attr(default: _T, factory: Optional[Callable[[], _T]] = ..., ) -> _T: ... @overload -def attr(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: ... -@overload def attr(default: Optional[_T] = ..., validator: Optional[_ValidatorArgType[_T]] = ..., repr: bool = ...,