From b45fec0277709c72fe42903425963a156b152067 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 5 Dec 2024 19:54:28 +0100 Subject: [PATCH 01/32] Add typing.Reader and Writer protocols --- Doc/library/typing.rst | 26 ++++++++++++- Doc/whatsnew/3.14.rst | 8 ++++ Lib/typing.py | 38 +++++++++++++++++++ ...-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst | 3 ++ 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 0fee782121b0af..bb9f0a560e3d0f 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2803,8 +2803,8 @@ with :func:`@runtime_checkable `. An ABC with one abstract method ``__round__`` that is covariant in its return type. -ABCs for working with IO ------------------------- +ABCs and Protocols for working with I/O +--------------------------------------- .. class:: IO TextIO @@ -2815,6 +2815,28 @@ ABCs for working with IO represent the types of I/O streams such as returned by :func:`open`. +The following protocols offer a simpler alternative for common use cases. They +are especially useful for annotating function and method arguments and are +decorated with :func:`@runtime_checkable `. + +.. class:: Reader[T] + .. method:: read(size=...) + .. method:: readline(size=...) + .. method:: __iter__() + +.. class:: Writer[T] + .. method:: write(o) + +For example:: + + def read_it(reader: Reader[str]): + assert reader.read(11) == "--marker--\n" + for line in reader: + print(line) + + def write_binary(writer: Writer[bytes]): + writer.write(b"Hello world!\n") + Functions and decorators ------------------------ diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index e83c509a025ab5..7c2688342710b7 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -607,6 +607,14 @@ tkinter (Contributed by Zhikang Yan in :gh:`126899`.) +typing +------ + +* Add protocols :class:`typing.Reader` and :class:`typing.Writer` as a simpler + alternatives to the pseudo-protocols :class:`typing.IO`, + :class:`typing.TextIO`, and :class:`typing.BinaryIO`. + + unicodedata ----------- diff --git a/Lib/typing.py b/Lib/typing.py index 5f3aacd877221c..cf862d0568c9c9 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -90,6 +90,7 @@ 'AsyncContextManager', # Structural checks, a.k.a. protocols. + 'Reader', 'Reversible', 'SupportsAbs', 'SupportsBytes', @@ -98,6 +99,7 @@ 'SupportsIndex', 'SupportsInt', 'SupportsRound', + 'Writer', # Concrete collection types. 'ChainMap', @@ -2925,6 +2927,42 @@ def __round__(self, ndigits: int = 0) -> T: pass +@runtime_checkable +class Reader[T](Iterable[T], Protocol): + """Protocol for simple I/O reader instances. + + This protocol only supports blocking I/O. + """ + + __slots__ = () + + def read(self, size: int = ..., /) -> T: + """Read data from the I/O stream and return it. + + If "size" is specified, at most "size" items (bytes/characters) will be + read. + """ + def readline(self, size: int = ..., /) -> T: + """Read a line of data from the I/O stream and return it. + + If "size" is specified, at most "size" items (bytes/characters) will be + read. + """ + + +@runtime_checkable +class Writer[T](Protocol): + """Protocol for simple I/O writer instances. + + This protocol only supports blocking I/O. + """ + + __slots__ = () + + def write(self, o: T, /) -> int: + """Write data to the I/O stream and return number of items written.""" + + def _make_nmtuple(name, fields, annotate_func, module, defaults = ()): nm_tpl = collections.namedtuple(name, fields, defaults=defaults, module=module) diff --git a/Misc/NEWS.d/next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst b/Misc/NEWS.d/next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst new file mode 100644 index 00000000000000..073326c7c798bc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst @@ -0,0 +1,3 @@ +Add protocols :class:`typing.Reader` and :class:`typing.Writer` as +alternatives to :class:`typing.IO`, :class:`typing.TextIO`, and +:class:`typing.BinaryIO`. From 1525e05c5490a1f3dcb9a26abbd7ff4554ad2e18 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 5 Dec 2024 20:02:32 +0100 Subject: [PATCH 02/32] Add a note about Iterable --- Doc/library/typing.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index bb9f0a560e3d0f..40c1f9fa77e384 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2837,6 +2837,13 @@ For example:: def write_binary(writer: Writer[bytes]): writer.write(b"Hello world!\n") +Also consider using :class:`collections.abc.Iterable` for iterating over +a stream:: + + def read_config(stream: Iterable[str]): + for line in stream: + ... + Functions and decorators ------------------------ From 7867ec18015ed813493feb67677bd51beb32d839 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 5 Dec 2024 20:06:37 +0100 Subject: [PATCH 03/32] Fix docs formatting --- Doc/library/typing.rst | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 40c1f9fa77e384..1096b119078e0a 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2820,22 +2820,30 @@ are especially useful for annotating function and method arguments and are decorated with :func:`@runtime_checkable `. .. class:: Reader[T] + + Protocol for reading from a file or other I/O stream. + .. method:: read(size=...) .. method:: readline(size=...) .. method:: __iter__() + For example:: + + def read_it(reader: Reader[str]): + assert reader.read(11) == "--marker--\n" + for line in reader: + print(line) + .. class:: Writer[T] - .. method:: write(o) -For example:: + Protocol for writing to a file or other I/O stream. - def read_it(reader: Reader[str]): - assert reader.read(11) == "--marker--\n" - for line in reader: - print(line) + .. method:: write(o) + + For example:: - def write_binary(writer: Writer[bytes]): - writer.write(b"Hello world!\n") + def write_binary(writer: Writer[bytes]): + writer.write(b"Hello world!\n") Also consider using :class:`collections.abc.Iterable` for iterating over a stream:: From 6a22a024d8b2d38e8351fa602e1998656b53312b Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 5 Dec 2024 20:09:10 +0100 Subject: [PATCH 04/32] Small wording improvements --- Doc/library/typing.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 1096b119078e0a..98873595ebad33 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2821,7 +2821,7 @@ decorated with :func:`@runtime_checkable `. .. class:: Reader[T] - Protocol for reading from a file or other I/O stream. + Protocol for reading from a file or other input stream. .. method:: read(size=...) .. method:: readline(size=...) @@ -2836,7 +2836,7 @@ decorated with :func:`@runtime_checkable `. .. class:: Writer[T] - Protocol for writing to a file or other I/O stream. + Protocol for writing to a file or other output stream. .. method:: write(o) @@ -2846,7 +2846,7 @@ decorated with :func:`@runtime_checkable `. writer.write(b"Hello world!\n") Also consider using :class:`collections.abc.Iterable` for iterating over -a stream:: +the lines of an input stream:: def read_config(stream: Iterable[str]): for line in stream: From 5d632a34a6825e4d0071f8c42b4a76dd4ae52a30 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 5 Dec 2024 20:23:37 +0100 Subject: [PATCH 05/32] Simplify the docs/improve formatting --- Doc/library/typing.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 98873595ebad33..9f1c99ff6ec28d 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2821,11 +2821,8 @@ decorated with :func:`@runtime_checkable `. .. class:: Reader[T] - Protocol for reading from a file or other input stream. - - .. method:: read(size=...) - .. method:: readline(size=...) - .. method:: __iter__() + Protocol for reading from a file or other input stream. Implementations + must support the ``read``, ``readline``, and ``__iter__`` methods. For example:: @@ -2836,9 +2833,8 @@ decorated with :func:`@runtime_checkable `. .. class:: Writer[T] - Protocol for writing to a file or other output stream. - - .. method:: write(o) + Protocol for writing to a file or other output stream. Implementations + must support the ``write`` method. For example:: From 4d50c2eda68c54044a45215d0cae873dd91d7458 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 5 Dec 2024 20:44:23 +0100 Subject: [PATCH 06/32] Explicitly document the methods Small improvements to the docstrings and signature --- Doc/library/typing.rst | 25 +++++++++++++++++++++---- Lib/typing.py | 9 +++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 9f1c99ff6ec28d..cb8cf077c224b4 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2821,8 +2821,21 @@ decorated with :func:`@runtime_checkable `. .. class:: Reader[T] - Protocol for reading from a file or other input stream. Implementations - must support the ``read``, ``readline``, and ``__iter__`` methods. + Protocol for reading from a file or other input stream. + + .. method:: read(size=...) + + Read data from the input stream and return it. If ``size`` is + specified, at most ``size`` items (bytes/characters) will be read. + + .. method:: readline(size=...) + + Read a line of data from the input stream and return it. If ``size`` is + specified, at most ``size`` items (bytes/characters) will be read. + + .. method:: __iter__() + + Read a line of data from the input stream and return it. For example:: @@ -2833,8 +2846,12 @@ decorated with :func:`@runtime_checkable `. .. class:: Writer[T] - Protocol for writing to a file or other output stream. Implementations - must support the ``write`` method. + Protocol for writing to a file or other output stream. + + .. method:: write(data) + + Write data to the output stream and return number of items + (bytes/characters) written. For example:: diff --git a/Lib/typing.py b/Lib/typing.py index cf862d0568c9c9..410d68981b01f3 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2937,13 +2937,14 @@ class Reader[T](Iterable[T], Protocol): __slots__ = () def read(self, size: int = ..., /) -> T: - """Read data from the I/O stream and return it. + """Read data from the input stream and return it. If "size" is specified, at most "size" items (bytes/characters) will be read. """ + def readline(self, size: int = ..., /) -> T: - """Read a line of data from the I/O stream and return it. + """Read a line of data from the input stream and return it. If "size" is specified, at most "size" items (bytes/characters) will be read. @@ -2959,8 +2960,8 @@ class Writer[T](Protocol): __slots__ = () - def write(self, o: T, /) -> int: - """Write data to the I/O stream and return number of items written.""" + def write(self, data: T, /) -> int: + """Write data to the output stream and return number of items written.""" def _make_nmtuple(name, fields, annotate_func, module, defaults = ()): From 1e1ea4154471a98d0d6582c554f432f5fc7a373d Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 6 Dec 2024 11:50:08 +0100 Subject: [PATCH 07/32] Mark protocol members as abstract --- Lib/typing.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/typing.py b/Lib/typing.py index 410d68981b01f3..7008a988cfdd47 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2936,6 +2936,7 @@ class Reader[T](Iterable[T], Protocol): __slots__ = () + @abstractmethod def read(self, size: int = ..., /) -> T: """Read data from the input stream and return it. @@ -2943,6 +2944,7 @@ def read(self, size: int = ..., /) -> T: read. """ + @abstractmethod def readline(self, size: int = ..., /) -> T: """Read a line of data from the input stream and return it. @@ -2960,6 +2962,7 @@ class Writer[T](Protocol): __slots__ = () + @abstractmethod def write(self, data: T, /) -> int: """Write data to the output stream and return number of items written.""" From 56a38a013315294a9b8cbddd0446f8c8c4114379 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 6 Dec 2024 12:01:55 +0100 Subject: [PATCH 08/32] Add .. versionadded --- Doc/library/typing.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index cb8cf077c224b4..ccce92161fa839 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2823,6 +2823,8 @@ decorated with :func:`@runtime_checkable `. Protocol for reading from a file or other input stream. + .. versionadded:: next + .. method:: read(size=...) Read data from the input stream and return it. If ``size`` is @@ -2848,6 +2850,8 @@ decorated with :func:`@runtime_checkable `. Protocol for writing to a file or other output stream. + .. versionadded:: next + .. method:: write(data) Write data to the output stream and return number of items From f2c331b2103f014b72bcce386383ba19e901b581 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 6 Dec 2024 12:03:41 +0100 Subject: [PATCH 09/32] Added slashes to documented signatures --- Doc/library/typing.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index ccce92161fa839..13835ef594c7e9 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2825,12 +2825,12 @@ decorated with :func:`@runtime_checkable `. .. versionadded:: next - .. method:: read(size=...) + .. method:: read(size=..., /) Read data from the input stream and return it. If ``size`` is specified, at most ``size`` items (bytes/characters) will be read. - .. method:: readline(size=...) + .. method:: readline(size=..., /) Read a line of data from the input stream and return it. If ``size`` is specified, at most ``size`` items (bytes/characters) will be read. @@ -2852,7 +2852,7 @@ decorated with :func:`@runtime_checkable `. .. versionadded:: next - .. method:: write(data) + .. method:: write(data, /) Write data to the output stream and return number of items (bytes/characters) written. From 6764b6ae573d65c654ba0ee1140f99c4329b7358 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 6 Dec 2024 12:04:52 +0100 Subject: [PATCH 10/32] Fix overindentation --- Doc/library/typing.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 13835ef594c7e9..d267a63df075a6 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2827,17 +2827,17 @@ decorated with :func:`@runtime_checkable `. .. method:: read(size=..., /) - Read data from the input stream and return it. If ``size`` is - specified, at most ``size`` items (bytes/characters) will be read. + Read data from the input stream and return it. If ``size`` is + specified, at most ``size`` items (bytes/characters) will be read. .. method:: readline(size=..., /) - Read a line of data from the input stream and return it. If ``size`` is - specified, at most ``size`` items (bytes/characters) will be read. + Read a line of data from the input stream and return it. If ``size`` is + specified, at most ``size`` items (bytes/characters) will be read. .. method:: __iter__() - Read a line of data from the input stream and return it. + Read a line of data from the input stream and return it. For example:: From 022acaa9a4fc2a67ee6fe8f754b305904504bb92 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 6 Dec 2024 12:07:14 +0100 Subject: [PATCH 11/32] Fix documentation of Reader.__iter__() --- Doc/library/typing.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index d267a63df075a6..efd72fc21146d3 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2837,7 +2837,8 @@ decorated with :func:`@runtime_checkable `. .. method:: __iter__() - Read a line of data from the input stream and return it. + Return an :class:`collections.abc.Iterator` over the lines of data + in the input stream. For example:: From b86073dcc1c9956e1925c3661d875da7edbbc71b Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Fri, 6 Dec 2024 13:07:42 +0100 Subject: [PATCH 12/32] Remove the @runtime_checkable flags --- Lib/typing.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index 7008a988cfdd47..b269b57c8bda2a 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2927,7 +2927,6 @@ def __round__(self, ndigits: int = 0) -> T: pass -@runtime_checkable class Reader[T](Iterable[T], Protocol): """Protocol for simple I/O reader instances. @@ -2953,7 +2952,6 @@ def readline(self, size: int = ..., /) -> T: """ -@runtime_checkable class Writer[T](Protocol): """Protocol for simple I/O writer instances. From 1f42b2182aa42e424b7394fbfefaa0a48faecf19 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 11:33:12 +0100 Subject: [PATCH 13/32] Remove Reader.__iter__() and readline() --- Doc/library/typing.rst | 15 ++------------- Lib/typing.py | 10 +--------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index efd72fc21146d3..71aeb7caa38328 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2830,22 +2830,11 @@ decorated with :func:`@runtime_checkable `. Read data from the input stream and return it. If ``size`` is specified, at most ``size`` items (bytes/characters) will be read. - .. method:: readline(size=..., /) - - Read a line of data from the input stream and return it. If ``size`` is - specified, at most ``size`` items (bytes/characters) will be read. - - .. method:: __iter__() - - Return an :class:`collections.abc.Iterator` over the lines of data - in the input stream. - For example:: def read_it(reader: Reader[str]): - assert reader.read(11) == "--marker--\n" - for line in reader: - print(line) + data = reader.read(11) + assert isinstance(data, str) .. class:: Writer[T] diff --git a/Lib/typing.py b/Lib/typing.py index 1a5293974470cf..e6146a9f785674 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -2931,7 +2931,7 @@ def __round__(self, ndigits: int = 0) -> T: pass -class Reader[T](Iterable[T], Protocol): +class Reader[T](Protocol): """Protocol for simple I/O reader instances. This protocol only supports blocking I/O. @@ -2947,14 +2947,6 @@ def read(self, size: int = ..., /) -> T: read. """ - @abstractmethod - def readline(self, size: int = ..., /) -> T: - """Read a line of data from the input stream and return it. - - If "size" is specified, at most "size" items (bytes/characters) will be - read. - """ - class Writer[T](Protocol): """Protocol for simple I/O writer instances. From 0325f5aba0960f81eda4bf156d85bbbf95a6333e Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 12:21:12 +0100 Subject: [PATCH 14/32] Move protocols to io --- Doc/library/io.rst | 43 +++++++++++++++++++++++++++++++++ Doc/library/typing.rst | 54 +++++++++++------------------------------- Lib/io.py | 50 +++++++++++++++++++++++++++++++++++++- Lib/typing.py | 32 ------------------------- 4 files changed, 106 insertions(+), 73 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 0d8cc5171d5476..f1c5824e19a470 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1147,6 +1147,49 @@ Text I/O It inherits from :class:`codecs.IncrementalDecoder`. +Static Typing +------------- + +The following protocols can be used for annotating function and method +arguments for simple stream reading or writing operations. They are decorated +with :func:`@runtime_checkable `. + +.. class:: Reader[T] + + Protocol for reading from a file or other input stream. + + .. versionadded:: next + + .. method:: read(size=..., /) + + Read data from the input stream and return it. If ``size`` is + specified, at most ``size`` items (bytes/characters) will be read. + + For example:: + + def read_it(reader: Reader[str]): + data = reader.read(11) + assert isinstance(data, str) + +.. class:: Writer[T] + + Protocol for writing to a file or other output stream. + + .. versionadded:: next + + .. method:: write(data, /) + + Write data to the output stream and return number of items + (bytes/characters) written. + + For example:: + + def write_binary(writer: Writer[bytes]): + writer.write(b"Hello world!\n") + +See :ref:`typing-io` for other I/O related protocols and classes used for +static type checking. + Performance ----------- diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index 71aeb7caa38328..fe1db3784170ee 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2803,54 +2803,28 @@ with :func:`@runtime_checkable `. An ABC with one abstract method ``__round__`` that is covariant in its return type. +.. _typing-io: + ABCs and Protocols for working with I/O --------------------------------------- -.. class:: IO - TextIO - BinaryIO +.. class:: IO[AnyStr] + TextIO[AnyStr] + BinaryIO[AnyStr] - Generic type ``IO[AnyStr]`` and its subclasses ``TextIO(IO[str])`` + Generic class ``IO[AnyStr]`` and its subclasses ``TextIO(IO[str])`` and ``BinaryIO(IO[bytes])`` represent the types of I/O streams such as returned by - :func:`open`. - -The following protocols offer a simpler alternative for common use cases. They -are especially useful for annotating function and method arguments and are -decorated with :func:`@runtime_checkable `. - -.. class:: Reader[T] - - Protocol for reading from a file or other input stream. - - .. versionadded:: next - - .. method:: read(size=..., /) - - Read data from the input stream and return it. If ``size`` is - specified, at most ``size`` items (bytes/characters) will be read. - - For example:: - - def read_it(reader: Reader[str]): - data = reader.read(11) - assert isinstance(data, str) - -.. class:: Writer[T] - - Protocol for writing to a file or other output stream. + :func:`open`. Please note that these classes are not protocols, and + their interface is fairly broad. - .. versionadded:: next - - .. method:: write(data, /) - - Write data to the output stream and return number of items - (bytes/characters) written. - - For example:: +The protocols :class:`.io.Reader` and :class:`.io.Writer` offer a simpler +alternative for argument types, when only the ``read()`` or ``write()`` +methods are accessed, respectively:: - def write_binary(writer: Writer[bytes]): - writer.write(b"Hello world!\n") + def read_and_write(reader: Reader[str], writer: Writer[bytes]): + data = reader.read() + writer.write(data.encode()) Also consider using :class:`collections.abc.Iterable` for iterating over the lines of an input stream:: diff --git a/Lib/io.py b/Lib/io.py index f0e2fa15d5abcf..c4ed29c4b5af07 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -46,12 +46,14 @@ "BufferedReader", "BufferedWriter", "BufferedRWPair", "BufferedRandom", "TextIOBase", "TextIOWrapper", "UnsupportedOperation", "SEEK_SET", "SEEK_CUR", "SEEK_END", - "DEFAULT_BUFFER_SIZE", "text_encoding", "IncrementalNewlineDecoder"] + "DEFAULT_BUFFER_SIZE", "text_encoding", "IncrementalNewlineDecoder", + "Reader", "Writer"] import _io import abc +from _collections_abc import _check_methods from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation, open, open_code, FileIO, BytesIO, StringIO, BufferedReader, BufferedWriter, BufferedRWPair, BufferedRandom, @@ -97,3 +99,49 @@ class TextIOBase(_io._TextIOBase, IOBase): pass else: RawIOBase.register(_WindowsConsoleIO) + +# +# Static Typing Support +# + + +class Reader[T](abc.ABC): + """Protocol for simple I/O reader instances. + + This protocol only supports blocking I/O. + """ + + __slots__ = () + + @abstractmethod + def read(self, size: int = ..., /) -> T: + """Read data from the input stream and return it. + + If "size" is specified, at most "size" items (bytes/characters) will be + read. + """ + + @classmethod + def __subclasshook__(cls, C): + if cls is Reader: + return _check_methods(C, "read") + return NotImplemented + + +class Writer[T](abc.ABC): + """Protocol for simple I/O writer instances. + + This protocol only supports blocking I/O. + """ + + __slots__ = () + + @abstractmethod + def write(self, data: T, /) -> int: + """Write data to the output stream and return number of items written.""" + + @classmethod + def __subclasshook__(cls, C): + if cls is Writer: + return _check_methods(C, "write") + return NotImplemented diff --git a/Lib/typing.py b/Lib/typing.py index e6146a9f785674..66570db7a5bd74 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -90,7 +90,6 @@ 'AsyncContextManager', # Structural checks, a.k.a. protocols. - 'Reader', 'Reversible', 'SupportsAbs', 'SupportsBytes', @@ -99,7 +98,6 @@ 'SupportsIndex', 'SupportsInt', 'SupportsRound', - 'Writer', # Concrete collection types. 'ChainMap', @@ -2931,36 +2929,6 @@ def __round__(self, ndigits: int = 0) -> T: pass -class Reader[T](Protocol): - """Protocol for simple I/O reader instances. - - This protocol only supports blocking I/O. - """ - - __slots__ = () - - @abstractmethod - def read(self, size: int = ..., /) -> T: - """Read data from the input stream and return it. - - If "size" is specified, at most "size" items (bytes/characters) will be - read. - """ - - -class Writer[T](Protocol): - """Protocol for simple I/O writer instances. - - This protocol only supports blocking I/O. - """ - - __slots__ = () - - @abstractmethod - def write(self, data: T, /) -> int: - """Write data to the output stream and return number of items written.""" - - def _make_nmtuple(name, fields, annotate_func, module, defaults = ()): nm_tpl = collections.namedtuple(name, fields, defaults=defaults, module=module) From 632511a7aa04971136db9912bceecc8af21f80b7 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 12:23:19 +0100 Subject: [PATCH 15/32] Update whatsnew --- Doc/whatsnew/3.14.rst | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 88e0847d35d676..bff3583f08f667 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -597,6 +597,11 @@ io :exc:`BlockingIOError` if the operation cannot immediately return bytes. (Contributed by Giovanni Siragusa in :gh:`109523`.) +* Add protocols :class:`io.Reader` and :class:`io.Writer` as a simpler + alternatives to the pseudo-protocols :class:`typing.IO`, + :class:`typing.TextIO`, and :class:`typing.BinaryIO`. + (Contributed by Sebastian Rittau in :gh:`127648`.) + json ---- @@ -865,15 +870,6 @@ turtle (Contributed by Marie Roald and Yngve Mardal Moe in :gh:`126350`.) -typing ------- - -* Add protocols :class:`typing.Reader` and :class:`typing.Writer` as a simpler - alternatives to the pseudo-protocols :class:`typing.IO`, - :class:`typing.TextIO`, and :class:`typing.BinaryIO`. - (Contributed by Sebastian Rittau in :gh:`127648`.) - - unicodedata ----------- From 3b384f903da618466d1d8b4bfdda5f0bb34d1a28 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 12:24:03 +0100 Subject: [PATCH 16/32] Update NEWS file --- .../next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst b/Misc/NEWS.d/next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst index 073326c7c798bc..8f0b812dcab639 100644 --- a/Misc/NEWS.d/next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst +++ b/Misc/NEWS.d/next/Library/2024-12-05-19-54-16.gh-issue-127647.Xd78Vs.rst @@ -1,3 +1,3 @@ -Add protocols :class:`typing.Reader` and :class:`typing.Writer` as +Add protocols :class:`io.Reader` and :class:`io.Writer` as alternatives to :class:`typing.IO`, :class:`typing.TextIO`, and :class:`typing.BinaryIO`. From 5bdb4cc6dc0d21e33ac757095bf85d40f371e6fc Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 12:29:07 +0100 Subject: [PATCH 17/32] Fix abstractmethod import --- Lib/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/io.py b/Lib/io.py index c4ed29c4b5af07..c7a45816358efd 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -113,7 +113,7 @@ class Reader[T](abc.ABC): __slots__ = () - @abstractmethod + @abc.abstractmethod def read(self, size: int = ..., /) -> T: """Read data from the input stream and return it. @@ -136,7 +136,7 @@ class Writer[T](abc.ABC): __slots__ = () - @abstractmethod + @abc.abstractmethod def write(self, data: T, /) -> int: """Write data to the output stream and return number of items written.""" From 35dcaf441c01718d9ef0da4dd927ea61ad96987f Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 12:34:48 +0100 Subject: [PATCH 18/32] Fix runtime_checkable link in docs --- Doc/library/io.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index f1c5824e19a470..6a57a9e974b691 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1152,7 +1152,7 @@ Static Typing The following protocols can be used for annotating function and method arguments for simple stream reading or writing operations. They are decorated -with :func:`@runtime_checkable `. +with :func:`@typing.runtime_checkable `. .. class:: Reader[T] From 5584a5724f2a490488a02f79772d4978b7ddd362 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 12:39:47 +0100 Subject: [PATCH 19/32] Add Reader and Writer to proto allowlist --- Lib/typing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/typing.py b/Lib/typing.py index 66570db7a5bd74..7484696020eedc 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1948,6 +1948,7 @@ def _allow_reckless_class_checks(depth=2): 'Reversible', 'Buffer', ], 'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'], + 'io': ['Reader', 'Writer'], 'os': ['PathLike'], } From af81301db1b81b05319af7dff2d1b080d1680b66 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 12:52:02 +0100 Subject: [PATCH 20/32] Import Reader and Writer into _pyio --- Lib/_pyio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index f7370dff19efc8..e915e5b138a623 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -16,7 +16,7 @@ _setmode = None import io -from io import (__all__, SEEK_SET, SEEK_CUR, SEEK_END) # noqa: F401 +from io import (__all__, SEEK_SET, SEEK_CUR, SEEK_END, Reader, Writer) # noqa: F401 valid_seek_flags = {0, 1, 2} # Hardwired values if hasattr(os, 'SEEK_HOLE') : From 5a8b915700e2ea3a24c7e21c3bf2b5a5a1e62d0c Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 13:33:00 +0100 Subject: [PATCH 21/32] Import _collections_abc dynamically --- Lib/io.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/io.py b/Lib/io.py index c7a45816358efd..248d6090decde3 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -53,7 +53,6 @@ import _io import abc -from _collections_abc import _check_methods from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation, open, open_code, FileIO, BytesIO, StringIO, BufferedReader, BufferedWriter, BufferedRWPair, BufferedRandom, @@ -123,6 +122,7 @@ def read(self, size: int = ..., /) -> T: @classmethod def __subclasshook__(cls, C): + from _collections_abc import _check_methods if cls is Reader: return _check_methods(C, "read") return NotImplemented @@ -142,6 +142,7 @@ def write(self, data: T, /) -> int: @classmethod def __subclasshook__(cls, C): + from _collections_abc import _check_methods if cls is Writer: return _check_methods(C, "write") return NotImplemented From 577b8931d361cb9857ee8f88ae38bccc4c53ca6a Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 13:55:03 +0100 Subject: [PATCH 22/32] Use metaclass instead of deriving from `ABC` --- Lib/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/io.py b/Lib/io.py index 248d6090decde3..3a0c62bdf56a0b 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -104,7 +104,7 @@ class TextIOBase(_io._TextIOBase, IOBase): # -class Reader[T](abc.ABC): +class Reader[T](metaclass=abc.ABCMeta): """Protocol for simple I/O reader instances. This protocol only supports blocking I/O. @@ -128,7 +128,7 @@ def __subclasshook__(cls, C): return NotImplemented -class Writer[T](abc.ABC): +class Writer[T](metaclass=abc.ABCMeta): """Protocol for simple I/O writer instances. This protocol only supports blocking I/O. From cedfa4267fc8b5f7f37a9c725851c754b93bb303 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 14:00:57 +0100 Subject: [PATCH 23/32] Use __class_getitem__ instead of making the class generic --- Lib/io.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/io.py b/Lib/io.py index 3a0c62bdf56a0b..b1f57844ceff07 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -103,8 +103,10 @@ class TextIOBase(_io._TextIOBase, IOBase): # Static Typing Support # +GenericAlias = type(list[int]) -class Reader[T](metaclass=abc.ABCMeta): + +class Reader(metaclass=abc.ABCMeta): """Protocol for simple I/O reader instances. This protocol only supports blocking I/O. @@ -127,8 +129,10 @@ def __subclasshook__(cls, C): return _check_methods(C, "read") return NotImplemented + __class_getitem__ = classmethod(GenericAlias) + -class Writer[T](metaclass=abc.ABCMeta): +class Writer(metaclass=abc.ABCMeta): """Protocol for simple I/O writer instances. This protocol only supports blocking I/O. @@ -146,3 +150,5 @@ def __subclasshook__(cls, C): if cls is Writer: return _check_methods(C, "write") return NotImplemented + + __class_getitem__ = classmethod(GenericAlias) From a0b9e475662fc7bb0c633cf4ff5b9b3d5bd9ca2b Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 14:08:49 +0100 Subject: [PATCH 24/32] Remove type annotations --- Lib/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/io.py b/Lib/io.py index b1f57844ceff07..45bccc91030968 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -115,7 +115,7 @@ class Reader(metaclass=abc.ABCMeta): __slots__ = () @abc.abstractmethod - def read(self, size: int = ..., /) -> T: + def read(self, size=..., /): """Read data from the input stream and return it. If "size" is specified, at most "size" items (bytes/characters) will be @@ -141,7 +141,7 @@ class Writer(metaclass=abc.ABCMeta): __slots__ = () @abc.abstractmethod - def write(self, data: T, /) -> int: + def write(self, data, /): """Write data to the output stream and return number of items written.""" @classmethod From 53a22502bfcb5c59108215749f496ef9886dc721 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Tue, 25 Feb 2025 14:34:16 +0100 Subject: [PATCH 25/32] Move import back to top level --- Lib/io.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/io.py b/Lib/io.py index 45bccc91030968..33d86556d1c05d 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -53,6 +53,7 @@ import _io import abc +from _collections_abc import _check_methods from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation, open, open_code, FileIO, BytesIO, StringIO, BufferedReader, BufferedWriter, BufferedRWPair, BufferedRandom, @@ -124,7 +125,6 @@ def read(self, size=..., /): @classmethod def __subclasshook__(cls, C): - from _collections_abc import _check_methods if cls is Reader: return _check_methods(C, "read") return NotImplemented @@ -146,7 +146,6 @@ def write(self, data, /): @classmethod def __subclasshook__(cls, C): - from _collections_abc import _check_methods if cls is Writer: return _check_methods(C, "write") return NotImplemented From ca72c1948f5bb9205beac4c3ad98bc5b3b8384eb Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 27 Feb 2025 12:55:46 +0100 Subject: [PATCH 26/32] Fix doc reference to decorator Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/io.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 6a57a9e974b691..67a5462ed5d70f 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1152,7 +1152,7 @@ Static Typing The following protocols can be used for annotating function and method arguments for simple stream reading or writing operations. They are decorated -with :func:`@typing.runtime_checkable `. +with :deco:`typing.runtime_checkable`. .. class:: Reader[T] From 3b5975ec03b89481afe724d0b647f34e208225cb Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 27 Feb 2025 13:08:31 +0100 Subject: [PATCH 27/32] Fix references in docs Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/typing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index fe1db3784170ee..dcee90e24f987c 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2818,7 +2818,7 @@ ABCs and Protocols for working with I/O :func:`open`. Please note that these classes are not protocols, and their interface is fairly broad. -The protocols :class:`.io.Reader` and :class:`.io.Writer` offer a simpler +The protocols :class:`io.Reader` and :class:`io.Writer` offer a simpler alternative for argument types, when only the ``read()`` or ``write()`` methods are accessed, respectively:: From 96080fed8c1b37d659c77df8435b75fa4f635871 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 27 Feb 2025 13:20:49 +0100 Subject: [PATCH 28/32] Split signature Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Doc/library/io.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index 67a5462ed5d70f..a910958df5d057 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1160,7 +1160,8 @@ with :deco:`typing.runtime_checkable`. .. versionadded:: next - .. method:: read(size=..., /) + .. method:: read() + read(size, /) Read data from the input stream and return it. If ``size`` is specified, at most ``size`` items (bytes/characters) will be read. From 37233706faae5d4f76a1a105553d151560e60fca Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 27 Feb 2025 13:38:34 +0100 Subject: [PATCH 29/32] Document that Reader and Writer are generic --- Doc/library/io.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index a910958df5d057..e9cca9187f714b 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1156,7 +1156,9 @@ with :deco:`typing.runtime_checkable`. .. class:: Reader[T] - Protocol for reading from a file or other input stream. + Generic protocol for reading from a file or other input stream. ``T`` will + usually be :class:`str` or :class:`bytes`, but can be any type that is + read from the stream. .. versionadded:: next @@ -1174,7 +1176,9 @@ with :deco:`typing.runtime_checkable`. .. class:: Writer[T] - Protocol for writing to a file or other output stream. + Generic protocol for writing to a file or other output stream. ``T`` will + usually be :class:`str` or :class:`bytes`, but can be any type that can be + written to the stream. .. versionadded:: next From 76003a85474409d0939c21b814663b1fbaeee650 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 27 Feb 2025 13:47:47 +0100 Subject: [PATCH 30/32] Add tests --- Lib/test/test_io.py | 18 ++++++++++++++++++ Lib/test/test_typing.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index e59d3977df4134..3b8ff1d20030b3 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -4916,6 +4916,24 @@ class PySignalsTest(SignalsTest): test_reentrant_write_text = None +class ProtocolsTest(unittest.TestCase): + class MyReader: + def read(self, sz=-1): + return b"" + + class MyWriter: + def write(self, b: bytes): + pass + + def test_reader_subclass(self): + self.assertIsSubclass(MyReader, io.Reader[bytes]) + self.assertNotIsSubclass(str, io.Reader[bytes]) + + def test_writer_subclass(self): + self.assertIsSubclass(MyWriter, io.Writer[bytes]) + self.assertNotIsSubclass(str, io.Writer[bytes]) + + def load_tests(loader, tests, pattern): tests = (CIOTest, PyIOTest, APIMismatchTest, CBufferedReaderTest, PyBufferedReaderTest, diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 591fb860eee1e0..4aeb6215f8c624 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4302,6 +4302,40 @@ def __release_buffer__(self, mv: memoryview) -> None: self.assertNotIsSubclass(C, ReleasableBuffer) self.assertNotIsInstance(C(), ReleasableBuffer) + def test_io_reader_protocol_allowed(self): + @runtime_checkable + class CustomReader(io.Reader[bytes], Protocol): + def close(self): ... + + class A: pass + class B: + def read(self, sz=-1): + return b"" + def close(self): + pass + + self.assertIsSubclass(B, CustomReader) + self.assertIsInstance(B(), CustomReader) + self.assertNotIsSubclass(A, CustomReader) + self.assertNotIsInstance(A(), CustomReader) + + def test_io_writer_protocol_allowed(self): + @runtime_checkable + class CustomWriter(io.Writer[bytes], Protocol): + def close(self): ... + + class A: pass + class B: + def write(self, b): + pass + def close(self): + pass + + self.assertIsSubclass(B, CustomWriter) + self.assertIsInstance(B(), CustomWriter) + self.assertNotIsSubclass(A, CustomWriter) + self.assertNotIsInstance(A(), CustomWriter) + def test_builtin_protocol_allowlist(self): with self.assertRaises(TypeError): class CustomProtocol(TestCase, Protocol): From 43e23f005a83fde5366a772506cc7653b897d109 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 27 Feb 2025 13:55:01 +0100 Subject: [PATCH 31/32] Add missing import --- Lib/test/test_typing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 4aeb6215f8c624..d710fc31276688 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -6,6 +6,7 @@ from functools import lru_cache, wraps, reduce import gc import inspect +import io import itertools import operator import os From c6447708e1fa92988f63e624e493c692cc880291 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Thu, 27 Feb 2025 13:56:41 +0100 Subject: [PATCH 32/32] Doc fixes Co-authored-by: Alex Waygood --- Doc/library/io.rst | 11 ++++++----- Doc/library/typing.rst | 12 ++++++------ Lib/io.py | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Doc/library/io.rst b/Doc/library/io.rst index e9cca9187f714b..cb2182334e5063 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -1165,8 +1165,9 @@ with :deco:`typing.runtime_checkable`. .. method:: read() read(size, /) - Read data from the input stream and return it. If ``size`` is - specified, at most ``size`` items (bytes/characters) will be read. + Read data from the input stream and return it. If *size* is + specified, it should be an integer, and at most *size* items + (bytes/characters) will be read. For example:: @@ -1184,7 +1185,7 @@ with :deco:`typing.runtime_checkable`. .. method:: write(data, /) - Write data to the output stream and return number of items + Write *data* to the output stream and return the number of items (bytes/characters) written. For example:: @@ -1192,8 +1193,8 @@ with :deco:`typing.runtime_checkable`. def write_binary(writer: Writer[bytes]): writer.write(b"Hello world!\n") -See :ref:`typing-io` for other I/O related protocols and classes used for -static type checking. +See :ref:`typing-io` for other I/O related protocols and classes that can be +used for static type checking. Performance ----------- diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst index dcee90e24f987c..f128af05496e48 100644 --- a/Doc/library/typing.rst +++ b/Doc/library/typing.rst @@ -2822,16 +2822,16 @@ The protocols :class:`io.Reader` and :class:`io.Writer` offer a simpler alternative for argument types, when only the ``read()`` or ``write()`` methods are accessed, respectively:: - def read_and_write(reader: Reader[str], writer: Writer[bytes]): - data = reader.read() - writer.write(data.encode()) + def read_and_write(reader: Reader[str], writer: Writer[bytes]): + data = reader.read() + writer.write(data.encode()) Also consider using :class:`collections.abc.Iterable` for iterating over the lines of an input stream:: - def read_config(stream: Iterable[str]): - for line in stream: - ... + def read_config(stream: Iterable[str]): + for line in stream: + ... Functions and decorators ------------------------ diff --git a/Lib/io.py b/Lib/io.py index 33d86556d1c05d..e9fe619392e3d9 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -119,7 +119,7 @@ class Reader(metaclass=abc.ABCMeta): def read(self, size=..., /): """Read data from the input stream and return it. - If "size" is specified, at most "size" items (bytes/characters) will be + If *size* is specified, at most *size* items (bytes/characters) will be read. """ @@ -142,7 +142,7 @@ class Writer(metaclass=abc.ABCMeta): @abc.abstractmethod def write(self, data, /): - """Write data to the output stream and return number of items written.""" + """Write *data* to the output stream and return the number of items written.""" @classmethod def __subclasshook__(cls, C):