Skip to content

Commit 3d235f3

Browse files
Introduce _typeshed.WeakUnion
1 parent 342e384 commit 3d235f3

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

stdlib/_typeshed/__init__.pyi

+25
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ from os import PathLike
1010
from typing import AbstractSet, Any, Container, Generic, Iterable, Protocol, TypeVar, Union
1111
from typing_extensions import Final, Literal, final
1212

13+
_WU = TypeVar('_WU')
14+
15+
class WeakUnion(Generic[_WU], Any):
16+
"""
17+
Some functions can return different types depending on passed arguments. e.g.
18+
19+
* `builtins.open(name, 'rb')` returns `io.BufferedReader`, whereas
20+
* `builtins.open(name, 'wb')` returns `io.BufferedWriter`.
21+
22+
Typeshed attempts to model such scenarios accurately via `@typing.overload`,
23+
however with with such overloaded functions there is always the case
24+
that the return type cannot be determined statically, e.g:
25+
26+
def my_open(name: str, mode: str):
27+
return open(name, mode)
28+
29+
In such cases typeshed currently returns Any. While a Union return would be
30+
more accurate that would require all existing code that depends on the Any
31+
(and asserts the type safety in some other way) to be updated. WeakUnion lets
32+
typeshed annotate the semantic return type of such overloads in a backwards
33+
compatible manner. Type checkers that do not know about this typeshed-specific
34+
type will just treat it as Any, whereas tooling that knows about it can use
35+
the additional information to provide more type safety or better autocompletion.
36+
"""
37+
1338
_KT = TypeVar("_KT")
1439
_KT_co = TypeVar("_KT_co", covariant=True)
1540
_KT_contra = TypeVar("_KT_contra", contravariant=True)

stdlib/builtins.pyi

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ from _typeshed import (
2121
SupportsRichComparisonT,
2222
SupportsTrunc,
2323
SupportsWrite,
24+
WeakUnion,
2425
)
2526
from collections.abc import Callable
2627
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
@@ -1327,7 +1328,7 @@ def open(
13271328
newline: str | None = ...,
13281329
closefd: bool = ...,
13291330
opener: _Opener | None = ...,
1330-
) -> IO[Any]: ...
1331+
) -> IO[WeakUnion[str | bytes]]: ...
13311332
def ord(__c: str | bytes) -> int: ...
13321333

13331334
class _SupportsWriteAndFlush(SupportsWrite[_T_contra], Protocol[_T_contra]):

stdlib/subprocess.pyi

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import sys
2-
from _typeshed import Self, StrOrBytesPath
2+
from _typeshed import Self, StrOrBytesPath, WeakUnion
33
from types import TracebackType
44
from typing import IO, Any, AnyStr, Callable, Generic, Iterable, Mapping, Sequence, TypeVar, Union, overload
55
from typing_extensions import Literal
@@ -633,7 +633,7 @@ if sys.version_info >= (3, 7):
633633
encoding: str | None = ...,
634634
errors: str | None = ...,
635635
text: bool | None = ...,
636-
) -> Any: ... # morally: -> _TXT
636+
) -> WeakUnion[str | bytes]: ...
637637

638638
else:
639639
@overload
@@ -755,7 +755,7 @@ else:
755755
input: _TXT | None = ...,
756756
encoding: str | None = ...,
757757
errors: str | None = ...,
758-
) -> Any: ... # morally: -> _TXT
758+
) -> WeakUnion[str | bytes]: ...
759759

760760
PIPE: int
761761
STDOUT: int

0 commit comments

Comments
 (0)