Skip to content

Commit adeda24

Browse files
open: introduce concrete return types (#4146)
* make io classes inherit from typing IO classes This makes these classes usable if type annotations are given as "IO" or "TextIO". In the future, we'll then be able to move open() to return a concrete class instead (#3951). * open: introduce concrete return types Fixes #3951. We use the values of the "mode" and "buffering" arguments to figure out the concrete type open() will return at runtime. (Compare the CPython code in https://github.com/python/cpython/blob/master/Modules/_io/_iomodule.c#L231.)
1 parent d863210 commit adeda24

File tree

4 files changed

+156
-25
lines changed

4 files changed

+156
-25
lines changed

stdlib/2/__builtin__.pyi

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ from typing import (
55
TypeVar, Iterator, Iterable, NoReturn, overload, Container,
66
Sequence, MutableSequence, Mapping, MutableMapping, Tuple, List, Any, Dict, Callable, Generic,
77
Set, AbstractSet, FrozenSet, MutableSet, Sized, Reversible, SupportsInt, SupportsFloat, SupportsAbs,
8-
SupportsComplex, IO, BinaryIO, TextIO, Union,
8+
SupportsComplex, IO, BinaryIO, Union,
99
ItemsView, KeysView, ValuesView, ByteString, Optional, AnyStr, Type, Text,
1010
Protocol,
1111
)
12-
from abc import abstractmethod, ABCMeta
12+
from abc import ABCMeta
1313
from ast import mod, AST
14-
from io import _OpenBinaryMode, _OpenTextMode
14+
from io import (
15+
_OpenBinaryMode, _OpenTextMode, _OpenBinaryModeUpdating, _OpenBinaryModeWriting, _OpenBinaryModeReading,
16+
TextIOWrapper, FileIO, BufferedRandom, BufferedReader, BufferedWriter
17+
)
1518
from types import TracebackType, CodeType
1619
import sys
1720

@@ -1364,7 +1367,9 @@ if sys.version_info >= (3,):
13641367
_OpenFile = Union[str, bytes, int, _PathLike[Any]]
13651368
else:
13661369
_OpenFile = Union[str, bytes, int]
1370+
_Opener = Callable[[str, int], int]
13671371

1372+
# Text mode: always returns a TextIOWrapper
13681373
@overload
13691374
def open(
13701375
file: _OpenFile,
@@ -1374,19 +1379,71 @@ if sys.version_info >= (3,):
13741379
errors: Optional[str] = ...,
13751380
newline: Optional[str] = ...,
13761381
closefd: bool = ...,
1377-
opener: Optional[Callable[[str, int], int]] = ...,
1378-
) -> TextIO: ...
1382+
opener: Optional[_Opener] = ...,
1383+
) -> TextIOWrapper: ...
1384+
1385+
# Unbuffered binary mode: returns a FileIO
13791386
@overload
13801387
def open(
13811388
file: _OpenFile,
13821389
mode: _OpenBinaryMode,
1383-
buffering: int = ...,
1390+
buffering: Literal[0],
1391+
encoding: None = ...,
1392+
errors: None = ...,
1393+
newline: None = ...,
1394+
closefd: bool = ...,
1395+
opener: Optional[_Opener] = ...,
1396+
) -> FileIO: ...
1397+
1398+
# Buffering is on: return BufferedRandom, BufferedReader, or BufferedWriter
1399+
@overload
1400+
def open(
1401+
file: _OpenFile,
1402+
mode: _OpenBinaryModeUpdating,
1403+
buffering: Literal[-1, 1] = ...,
1404+
encoding: None = ...,
1405+
errors: None = ...,
1406+
newline: None = ...,
1407+
closefd: bool = ...,
1408+
opener: Optional[_Opener] = ...,
1409+
) -> BufferedRandom: ...
1410+
@overload
1411+
def open(
1412+
file: _OpenFile,
1413+
mode: _OpenBinaryModeWriting,
1414+
buffering: Literal[-1, 1] = ...,
13841415
encoding: None = ...,
13851416
errors: None = ...,
13861417
newline: None = ...,
13871418
closefd: bool = ...,
1388-
opener: Optional[Callable[[str, int], int]] = ...,
1419+
opener: Optional[_Opener] = ...,
1420+
) -> BufferedWriter: ...
1421+
@overload
1422+
def open(
1423+
file: _OpenFile,
1424+
mode: _OpenBinaryModeReading,
1425+
buffering: Literal[-1, 1] = ...,
1426+
encoding: None = ...,
1427+
errors: None = ...,
1428+
newline: None = ...,
1429+
closefd: bool = ...,
1430+
opener: Optional[_Opener] = ...,
1431+
) -> BufferedReader: ...
1432+
1433+
# Buffering cannot be determined: fall back to BinaryIO
1434+
@overload
1435+
def open(
1436+
file: _OpenFile,
1437+
mode: _OpenBinaryMode,
1438+
buffering: int,
1439+
encoding: None = ...,
1440+
errors: None = ...,
1441+
newline: None = ...,
1442+
closefd: bool = ...,
1443+
opener: Optional[_Opener] = ...,
13891444
) -> BinaryIO: ...
1445+
1446+
# Fallback if mode is not specified
13901447
@overload
13911448
def open(
13921449
file: _OpenFile,
@@ -1396,7 +1453,7 @@ if sys.version_info >= (3,):
13961453
errors: Optional[str] = ...,
13971454
newline: Optional[str] = ...,
13981455
closefd: bool = ...,
1399-
opener: Optional[Callable[[str, int], int]] = ...,
1456+
opener: Optional[_Opener] = ...,
14001457
) -> IO[Any]: ...
14011458

14021459
else:

stdlib/2/io.pyi

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,20 @@ _OpenTextMode = Literal[
2828
'a', 'a+', '+a', 'at', 'ta', 'at+', 'a+t', '+at', 'ta+', 't+a', '+ta',
2929
'U', 'rU', 'Ur', 'rtU', 'rUt', 'Urt', 'trU', 'tUr', 'Utr',
3030
]
31-
_OpenBinaryMode = Literal[
32-
'rb', 'br', 'rb+', 'r+b', '+rb', 'br+', 'b+r', '+br',
33-
'wb', 'bw', 'wb+', 'w+b', '+wb', 'bw+', 'b+w', '+bw',
34-
'ab', 'ba', 'ab+', 'a+b', '+ab', 'ba+', 'b+a', '+ba',
31+
_OpenBinaryModeUpdating = Literal[
32+
'rb+', 'r+b', '+rb', 'br+', 'b+r', '+br',
33+
'wb+', 'w+b', '+wb', 'bw+', 'b+w', '+bw',
34+
'ab+', 'a+b', '+ab', 'ba+', 'b+a', '+ba',
35+
]
36+
_OpenBinaryModeWriting = Literal[
37+
'wb', 'bw',
38+
'ab', 'ba',
39+
]
40+
_OpenBinaryModeReading = Literal[
41+
'rb', 'br',
3542
'rbU', 'rUb', 'Urb', 'brU', 'bUr', 'Ubr',
3643
]
44+
_OpenBinaryMode = Union[_OpenBinaryModeUpdating, _OpenBinaryModeReading, _OpenBinaryModeWriting]
3745

3846
def _OpenWrapper(file: Union[str, unicode, int],
3947
mode: unicode = ..., buffering: int = ..., encoding: unicode = ...,

stdlib/2and3/builtins.pyi

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ from typing import (
55
TypeVar, Iterator, Iterable, NoReturn, overload, Container,
66
Sequence, MutableSequence, Mapping, MutableMapping, Tuple, List, Any, Dict, Callable, Generic,
77
Set, AbstractSet, FrozenSet, MutableSet, Sized, Reversible, SupportsInt, SupportsFloat, SupportsAbs,
8-
SupportsComplex, IO, BinaryIO, TextIO, Union,
8+
SupportsComplex, IO, BinaryIO, Union,
99
ItemsView, KeysView, ValuesView, ByteString, Optional, AnyStr, Type, Text,
1010
Protocol,
1111
)
12-
from abc import abstractmethod, ABCMeta
12+
from abc import ABCMeta
1313
from ast import mod, AST
14-
from io import _OpenBinaryMode, _OpenTextMode
14+
from io import (
15+
_OpenBinaryMode, _OpenTextMode, _OpenBinaryModeUpdating, _OpenBinaryModeWriting, _OpenBinaryModeReading,
16+
TextIOWrapper, FileIO, BufferedRandom, BufferedReader, BufferedWriter
17+
)
1518
from types import TracebackType, CodeType
1619
import sys
1720

@@ -1364,7 +1367,9 @@ if sys.version_info >= (3,):
13641367
_OpenFile = Union[str, bytes, int, _PathLike[Any]]
13651368
else:
13661369
_OpenFile = Union[str, bytes, int]
1370+
_Opener = Callable[[str, int], int]
13671371

1372+
# Text mode: always returns a TextIOWrapper
13681373
@overload
13691374
def open(
13701375
file: _OpenFile,
@@ -1374,19 +1379,71 @@ if sys.version_info >= (3,):
13741379
errors: Optional[str] = ...,
13751380
newline: Optional[str] = ...,
13761381
closefd: bool = ...,
1377-
opener: Optional[Callable[[str, int], int]] = ...,
1378-
) -> TextIO: ...
1382+
opener: Optional[_Opener] = ...,
1383+
) -> TextIOWrapper: ...
1384+
1385+
# Unbuffered binary mode: returns a FileIO
13791386
@overload
13801387
def open(
13811388
file: _OpenFile,
13821389
mode: _OpenBinaryMode,
1383-
buffering: int = ...,
1390+
buffering: Literal[0],
1391+
encoding: None = ...,
1392+
errors: None = ...,
1393+
newline: None = ...,
1394+
closefd: bool = ...,
1395+
opener: Optional[_Opener] = ...,
1396+
) -> FileIO: ...
1397+
1398+
# Buffering is on: return BufferedRandom, BufferedReader, or BufferedWriter
1399+
@overload
1400+
def open(
1401+
file: _OpenFile,
1402+
mode: _OpenBinaryModeUpdating,
1403+
buffering: Literal[-1, 1] = ...,
1404+
encoding: None = ...,
1405+
errors: None = ...,
1406+
newline: None = ...,
1407+
closefd: bool = ...,
1408+
opener: Optional[_Opener] = ...,
1409+
) -> BufferedRandom: ...
1410+
@overload
1411+
def open(
1412+
file: _OpenFile,
1413+
mode: _OpenBinaryModeWriting,
1414+
buffering: Literal[-1, 1] = ...,
13841415
encoding: None = ...,
13851416
errors: None = ...,
13861417
newline: None = ...,
13871418
closefd: bool = ...,
1388-
opener: Optional[Callable[[str, int], int]] = ...,
1419+
opener: Optional[_Opener] = ...,
1420+
) -> BufferedWriter: ...
1421+
@overload
1422+
def open(
1423+
file: _OpenFile,
1424+
mode: _OpenBinaryModeReading,
1425+
buffering: Literal[-1, 1] = ...,
1426+
encoding: None = ...,
1427+
errors: None = ...,
1428+
newline: None = ...,
1429+
closefd: bool = ...,
1430+
opener: Optional[_Opener] = ...,
1431+
) -> BufferedReader: ...
1432+
1433+
# Buffering cannot be determined: fall back to BinaryIO
1434+
@overload
1435+
def open(
1436+
file: _OpenFile,
1437+
mode: _OpenBinaryMode,
1438+
buffering: int,
1439+
encoding: None = ...,
1440+
errors: None = ...,
1441+
newline: None = ...,
1442+
closefd: bool = ...,
1443+
opener: Optional[_Opener] = ...,
13891444
) -> BinaryIO: ...
1445+
1446+
# Fallback if mode is not specified
13901447
@overload
13911448
def open(
13921449
file: _OpenFile,
@@ -1396,7 +1453,7 @@ if sys.version_info >= (3,):
13961453
errors: Optional[str] = ...,
13971454
newline: Optional[str] = ...,
13981455
closefd: bool = ...,
1399-
opener: Optional[Callable[[str, int], int]] = ...,
1456+
opener: Optional[_Opener] = ...,
14001457
) -> IO[Any]: ...
14011458

14021459
else:

stdlib/3/io.pyi

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,22 @@ _OpenTextMode = Literal[
2929
'x', 'x+', '+x', 'xt', 'tx', 'xt+', 'x+t', '+xt', 'tx+', 't+x', '+tx',
3030
'U', 'rU', 'Ur', 'rtU', 'rUt', 'Urt', 'trU', 'tUr', 'Utr',
3131
]
32-
_OpenBinaryMode = Literal[
33-
'rb', 'br', 'rb+', 'r+b', '+rb', 'br+', 'b+r', '+br',
34-
'wb', 'bw', 'wb+', 'w+b', '+wb', 'bw+', 'b+w', '+bw',
35-
'ab', 'ba', 'ab+', 'a+b', '+ab', 'ba+', 'b+a', '+ba',
36-
'xb', 'bx', 'xb+', 'x+b', '+xb', 'bx+', 'b+x', '+bx',
32+
_OpenBinaryModeUpdating = Literal[
33+
'rb+', 'r+b', '+rb', 'br+', 'b+r', '+br',
34+
'wb+', 'w+b', '+wb', 'bw+', 'b+w', '+bw',
35+
'ab+', 'a+b', '+ab', 'ba+', 'b+a', '+ba',
36+
'xb+', 'x+b', '+xb', 'bx+', 'b+x', '+bx',
37+
]
38+
_OpenBinaryModeWriting = Literal[
39+
'wb', 'bw',
40+
'ab', 'ba',
41+
'xb', 'bx',
42+
]
43+
_OpenBinaryModeReading = Literal[
44+
'rb', 'br',
3745
'rbU', 'rUb', 'Urb', 'brU', 'bUr', 'Ubr',
3846
]
47+
_OpenBinaryMode = Union[_OpenBinaryModeUpdating, _OpenBinaryModeReading, _OpenBinaryModeWriting]
3948

4049
open = builtins.open
4150

0 commit comments

Comments
 (0)