8000 _io / io redo by tungol · Pull Request #12740 · python/typeshed · GitHub
[go: up one dir, main page]

Skip to content

_io / io redo #12740

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
_io / io redo
This is an updated version of #11187

In addition to shuffling defintions between files, these
things got tweaked:

 - Removed several redundant definitions of __enter__. These
   seem to be an artifact of using TypeVar instead of Self originally
 - seek and truncate have inconsistent variable names. They're also
   positional only, so they were in the stubtest allowlist before,
   but I went ahead and made them explicit in the stubs
 - BytesIO.readlines shouldn't have been on the allowlist in
   the first place. It differs not only in variable name, but
   also default value.
 - A big block of functions in TextIOWrapper were commented that
   mypy needed them, but I don't think it does anymore, unless
   there's a problem with subclassing TextIOWrapper that doesn't
   show up in typeshed itself. No indication in the history about that.
 - In the implementation, the concrete classes inherit from the
   private implementation _*IOBase classes, not the classes in io
   which are actually metaclasses, but they are registered to those
   metaclasses at runtime. It wasn't technically required for any reason,
   but I split the difference on this by keeping the _*IOBase classes
   in their base classes directly. I think it's a bit of a reminder
   of the actual implementation, and means that a stubtest check for
   inheritance will show that typeshed is adding to the base classes,
   rather than replacing the base class, and I think that's a little cleaner.

Partially related to #3968
  • Loading branch information
tungol committed Oct 5, 2024
commit f9b87720296fa8274312c2bcbd56c86fc6e6295e
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ extra-standard-library = [
"_dummy_threading",
"_heapq",
"_imp",
"_io",
"_json",
"_locale",
"_lsprof",
Expand Down
11 changes: 0 additions & 11 deletions stdlib/@tests/stubtest_allowlists/common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,6 @@ importlib.abc.Loader.exec_module # See Lib/importlib/_abc.py. Might be defined
importlib.abc.MetaPathFinder.find_spec # Not defined on the actual class, but expected to exist.
importlib.abc.PathEntryFinder.find_spec # Not defined on the actual class, but expected to exist.
importlib.machinery.ExtensionFileLoader.get_filename # Wrapped with _check_name decorator which changes runtime signature
io.BufferedRandom.truncate
io.BufferedReader.seek
io.BufferedReader.truncate
io.BufferedWriter.seek
io.BufferedWriter.truncate
io.BytesIO.readlines
io.BytesIO.seek # Parameter name for a positional-only param differs from its name in the inherited method
io.FileIO.seek
io.StringIO.seek
io.StringIO.truncate
io.TextIOWrapper.truncate
ipaddress.IPv4Interface.hostmask
ipaddress.IPv6Interface.hostmask
ipaddress._BaseNetwork.broadcast_address
Expand Down
1 change: 1 addition & 0 deletions stdlib/VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ _dummy_thread: 3.0-3.8
_dummy_threading: 3.0-3.8
_heapq: 3.0-
_imp: 3.0-
_io: 3.0-
_interpchannels: 3.13-
_interpqueues: 3.13-
_interpreters: 3.13-
Expand Down
218 changes: 218 additions & 0 deletions stdlib/_io.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import builtins
import codecs
import sys
from _typeshed import FileDescriptorOrPath, MaybeNone, ReadableBuffer, WriteableBuffer
from collections.abc import Callable, Iterable, Iterator
from io import BufferedIOBase, RawIOBase, TextIOBase, UnsupportedOperation as UnsupportedOperation
from os import _Opener
from types import TracebackType
from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only
from typing_extensions import Self

@type_check_only
class _WrappedBuffer(Protocol):
# "name" is wrapped by TextIOWrapper. Its type is inconsistent between
# the various I/O types, see the comments on TextIOWrapper.name and
# TextIO.name.
@property
def name(self) -> Any: ...
@property
def closed(self) -> bool: ...
def read(self, size: int = ..., /) -> ReadableBuffer: ...
# Optional: def read1(self, size: int, /) -> ReadableBuffer: ...
def write(self, b: bytes, /) -> object: ...
def flush(self) -> object: ...
def close(self) -> object: ...
def seekable(self) -> bool: ...
def readable(self) -> bool: ...
def writable(self) -> bool: ...
def truncate(self, size: int, /) -> int: ...
def fileno(self) -> int: ...
def isatty(self) -> bool: ...
# Optional: Only needs to be present if seekable() returns True.
# def seek(self, offset: Literal[0], whence: Literal[2]) -> int: ...
# def tell(self) -> int: ...

_T = TypeVar("_T")
_BufferT_co = TypeVar("_BufferT_co", bound=_WrappedBuffer, default=_WrappedBuffer, covariant=True)

DEFAULT_BUFFER_SIZE: Final = 8192

open = builtins.open
BlockingIOError = builtins.BlockingIOError

def open_code(path: str) -> IO[bytes]: ...

if sys.version_info >= (3, 10):
@overload
def text_encoding(encoding: None, stacklevel: int = 2, /) -> Literal["locale", "utf-8"]: ...
@overload
def text_encoding(encoding: _T, stacklevel: int = 2, /) -> _T: ...

class _IOBase:
def __iter__(self) -> Iterator[bytes]: ...
def __next__(self) -> bytes: ...
def __enter__(self) -> Self: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> None: ...
def close(self) -> None: ...
def fileno(self) -> int: ...
def flush(self) -> None: ...
def isatty(self) -> bool: ...
def readable(self) -> bool: ...
read: Callable[..., Any]
def readlines(self, hint: int = -1, /) -> list[bytes]: ...
def seek(self, offset: int, whence: int = ..., /) -> int: ...
def seekable(self) -> bool: ...
def tell(self) -> int: ...
def truncate(self, size: int | None = ..., /) -> int: ...
def writable(self) -> bool: ...
write: Callable[..., Any]
def writelines(self, lines: Iterable[ReadableBuffer], /) -> None: ...
def readline(self, size: int | None = -1, /) -> bytes: ...
def __del__(self) -> None: ...
@property
def closed(self) -> bool: ...
def _checkClosed(self) -> None: ... # undocumented

class _RawIOBase(_IOBase):
def readall(self) -> bytes: ...
# The following methods can return None if the file is in non-blocking mode
# and no data is available.
def readinto(self, buffer: WriteableBuffer, /) -> int | MaybeNone: ...
def write(self, b: ReadableBuffer, /) -> int | MaybeNone: ...
def read(self, size: int = -1, /) -> bytes | MaybeNone: ...

class FileIO(RawIOBase, _RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes
mode: str
# The type of "name" equals the argument passed in to the constructor,
# but that can make FileIO incompatible with other I/O types that assume
# "name" is a str. In the future, making FileIO generic might help.
name: Any
def __init__(
self, file: FileDescriptorOrPath, mode: str = ..., closefd: bool = ..., opener: _Opener | None = ...
) -> None: ...
@property
def closefd(self) -> bool: ...
# this is here so stubtest is happy with the argument names
def seek(self, pos: int, whence: int = ..., /) -> int: ...

class _BufferedIOBase(_IOBase):
def detach(self) -> RawIOBase: ...
def readinto(self, buffer: WriteableBuffer, /) -> int: ...
def write(self, buffer: ReadableBuffer, /) -> int: ...
def readinto1(self, buffer: WriteableBuffer, /) -> int: ...
def read(self, size: int | None = ..., /) -> bytes: ...
def read1(self, size: int = ..., /) -> bytes: ...

class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes
def __init__(self, initial_bytes: ReadableBuffer = ...) -> None: ...
# BytesIO does not contain a "name" field. This workaround is necessary
# to allow BytesIO sub-classes to add this field, as it is defined
# as a read-only property on IO[].
name: Any
def getvalue(self) -> bytes: ...
def getbuffer(self) -> memoryview: ...
def read1(self, size: int | None = -1, /) -> bytes: ...
def readlines(self, size: int | None = None, /) -> list[bytes]: ...
# this is here so stubtest is happy with the argument names
def seek(self, pos: int, whence: int = ..., /) -> int: ...

class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes
# technically has mode and name attributes, but they're just pass-throughs to
# self.raw.*
raw: RawIOBase
def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ...
def peek(self, size: int = 0, /) -> bytes: ...
# these are here so stubtest is happy with the argument names
def seek(self, target: int, whence: int = ..., /) -> int: ...
def truncate(self, pos: int | None = ..., /) -> int: ...

class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes
# technically has mode and name attributes, but they're just pass-throughs to
# self.raw.*
raw: RawIOBase
def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ...
def write(self, buffer: ReadableBuffer, /) -> int: ...
# these are here so stubtest is happy with the argument names
def seek(self, target: int, whence: int = ..., /) -> int: ...
def truncate(self, pos: int | None = ..., /) -> int: ...

class BufferedRWPair(BufferedIOBase, _BufferedIOBase):
def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = ...) -> None: ...
def peek(self, size: int = ..., /) -> bytes: ...

# The C implementation is not subclass of BufferedReader and BufferedWriter at runtime,
# but the implementation in _pyio is
class BufferedRandom(BufferedReader, BufferedWriter, BufferedIOBase, _BufferedIOBase): ... # type: ignore[misc] # incompatible definitions of methods in the base classes

class _TextIOBase(_IOBase):
encoding: str
errors: str | None
newlines: str | tuple[str, ...] | None
def __iter__(self) -> Iterator[str]: ... # type: ignore[override]
def __next__(self) -> str: ... # type: ignore[override]
def detach(self) -> BinaryIO: ...
def write(self, s: str, /) -> int: ...
def writelines(self, lines: Iterable[str], /) -> None: ... # type: ignore[override]
def readline(self, size: int = ..., /) -> str: ... # type: ignore[override]
def readlines(self, hint: int = -1, /) -> list[str]: ... # type: ignore[override]
def read(self, size: int | None = ..., /) -> str: ...

class TextIOWrapper(TextIOBase, _TextIOBase, TextIO, Generic[_BufferT_co]): # type: ignore[misc] # incompatible definitions of write in the base classes
def __init__(
self,
buffer: _BufferT_co,
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
line_buffering: bool = False,
write_through: bool = False,
) -> None: ...
# Equals the "buffer" argument passed in to the constructor.
@property
def buffer(self) -> _BufferT_co: ... # type: ignore[override]
@property
def line_buffering(self) -> bool: ...
@property
def name(self) -> str: ...
@property
def write_through(self) -> bool: ...
def reconfigure(
self,
*,
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
line_buffering: bool | None = None,
write_through: bool | None = None,
) -> None: ...
# Equals the "buffer" argument passed in to the constructor.
def detach(self) -> _BufferT_co: ... # type: ignore[override]
# TextIOWrapper's version of seek only supports a limited subset of
# operations.
def seek(self, cookie: int, whence: int = 0, /) -> int: ...
# this is here so stubtest is happy with the argument names
def truncate(self, pos: int | None = ..., /) -> int: ...

# The C implementation is not a subclass of TextIOWrapper at runtime,
# but the implementation in _pyio is
class StringIO(TextIOWrapper, TextIOBase, _TextIOBase): # type: ignore[misc] # incompatible definitions of write in the base classes
def __init__(self, initial_value: str | None = ..., newline: str | None = ...) -> None: ...
# StringIO does not contain a "name" field. This workaround is necessary
# to allow StringIO sub-classes to add this field, as it is defined
# as a read-only property on IO[].
name: Any
def getvalue(self) -> str: ...
# this is here so stubtest is happy with the argument names
def seek(self, pos: int, whence: int = ..., /) -> int: ...

# The C implementation isnot a subclass of codecs.IncrementalDecoder at runtime
# but the implementation in _pyio is
class IncrementalNewlineDecoder(codecs.IncrementalDecoder):
def __init__(self, decoder: codecs.IncrementalDecoder | None, translate: bool, errors: str = ...) -> None: ...
def decode(self, input: ReadableBuffer | str, final: bool = False) -> str: ...
@property
def newlines(self) -> str | tuple[str, ...] | None: ...
def setstate(self, state: tuple[bytes, int], /) -> None: ...
Loading
Loading
0