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): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy