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

Introduce _typeshed.WeakUnion #7437

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions stdlib/_typeshed/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@ from os import PathLike
from typing import AbstractSet, Any, Container, Generic, Iterable, Protocol, TypeVar, Union
from typing_extensions import Final, Literal, final

_WU = TypeVar("_WU")

class WeakUnion(Any, Generic[_WU]):
"""
Some functions can return different types depending on passed arguments. e.g.

* `builtins.open(name, 'rb')` returns `io.BufferedReader`, whereas
* `builtins.open(name, 'wb')` returns `io.BufferedWriter`.

Typeshed attempts to model such scenarios accurately via `@typing.overload`,
however with with such overloaded functions there is always the case
that the return type cannot be determined statically, e.g:

def my_open(name: str, mode: str):
return open(name, mode)

In such cases typeshed currently returns Any. While a Union return would be
more accurate that would require all existing code that depends on the Any
(and asserts the type safety in some other way) to be updated. WeakUnion lets
typeshed annotate the semantic return type of such overloads in a backwards
compatible manner. Type checkers that do not know about this typeshed-specific
type will just treat it as Any, whereas tooling that knows about it can use
the additional information to provide more type safety or better autocompletion.
"""

_KT = TypeVar("_KT")
_KT_co = TypeVar("_KT_co", covariant=True)
_KT_contra = TypeVar("_KT_contra", contravariant=True)
Expand Down
5 changes: 3 additions & 2 deletions stdlib/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ from _typeshed import (
SupportsRichComparisonT,
SupportsTrunc,
SupportsWrite,
WeakUnion,
)
from collections.abc import Callable
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
from types import CodeType, TracebackType, _Cell
from typing import (
IO,
AbstractSet,
Any,
BinaryIO,
Expand All @@ -49,6 +49,7 @@ from typing import (
SupportsFloat,
SupportsInt,
SupportsRound,
TextIO,
TypeVar,
Union,
overload,
Expand Down Expand Up @@ -1327,7 +1328,7 @@ def open(
newline: str | None = ...,
closefd: bool = ...,
opener: _Opener | None = ...,
) -> IO[Any]: ...
) -> WeakUnion[TextIO | BinaryIO]: ...
def ord(__c: str | bytes) -> int: ...

class _SupportsWriteAndFlush(SupportsWrite[_T_contra], Protocol[_T_contra]):
Expand Down
6 changes: 3 additions & 3 deletions stdlib/subprocess.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
from _typeshed import Self, StrOrBytesPath
from _typeshed import Self, StrOrBytesPath, WeakUnion
from types import TracebackType
from typing import IO, Any, AnyStr, Callable, Generic, Iterable, Mapping, Sequence, TypeVar, Union, overload
from typing_extensions import Literal
Expand Down Expand Up @@ -633,7 +633,7 @@ if sys.version_info >= (3, 7):
encoding: str | None = ...,
errors: str | None = ...,
text: bool | None = ...,
) -> Any: ... # morally: -> _TXT
) -> WeakUnion[str | bytes]: ...

else:
@overload
Expand Down Expand Up @@ -755,7 +755,7 @@ else:
input: _TXT | None = ...,
encoding: str | None = ...,
errors: str | None = ...,
) -> Any: ... # morally: -> _TXT
) -> WeakUnion[str | bytes]: ...

PIPE: int
STDOUT: int
Expand Down