https://github.com/python/cpython/commit/daa2578dc04cce99545e72acc8431929519c04fc
commit: daa2578dc04cce99545e72acc8431929519c04fc
branch: main
author: Jason R. Coombs <[email protected]>
committer: jaraco <[email protected]>
date: 2026-04-11T22:25:20Z
summary:
gh-127012: `Traversable.read_text` now allows/solicits an `errors` parameter.
(#148401)
Applies changes from importlib_resources 6.5.2.
files:
A Lib/test/test_importlib/resources/test_util.py
A Misc/NEWS.d/next/Library/2026-04-11-17-28-06.gh-issue-127012.h3rLYS.rst
M Lib/importlib/resources/__init__.py
M Lib/importlib/resources/_common.py
M Lib/importlib/resources/_functional.py
M Lib/importlib/resources/abc.py
M Lib/importlib/resources/readers.py
M Lib/test/test_importlib/resources/_path.py
M Lib/test/test_importlib/resources/test_compatibilty_files.py
M Lib/test/test_importlib/resources/test_contents.py
M Lib/test/test_importlib/resources/test_custom.py
M Lib/test/test_importlib/resources/test_files.py
M Lib/test/test_importlib/resources/test_functional.py
M Lib/test/test_importlib/resources/test_open.py
M Lib/test/test_importlib/resources/test_path.py
M Lib/test/test_importlib/resources/test_read.py
M Lib/test/test_importlib/resources/test_reader.py
M Lib/test/test_importlib/resources/test_resource.py
M Lib/test/test_importlib/resources/util.py
diff --git a/Lib/importlib/resources/__init__.py
b/Lib/importlib/resources/__init__.py
index 723c9f9eb33ce1..27d6c7f89307ef 100644
--- a/Lib/importlib/resources/__init__.py
+++ b/Lib/importlib/resources/__init__.py
@@ -8,12 +8,11 @@
"""
from ._common import (
+ Anchor,
+ Package,
as_file,
files,
- Package,
- Anchor,
)
-
from ._functional import (
contents,
is_resource,
@@ -23,10 +22,8 @@
read_binary,
read_text,
)
-
from .abc import ResourceReader
-
__all__ = [
'Package',
'Anchor',
diff --git a/Lib/importlib/resources/_common.py
b/Lib/importlib/resources/_common.py
index d16ebe4520fbbf..40eec742aeb70a 100644
--- a/Lib/importlib/resources/_common.py
+++ b/Lib/importlib/resources/_common.py
@@ -1,14 +1,14 @@
-import os
-import pathlib
-import tempfile
-import functools
import contextlib
-import types
+import functools
import importlib
import inspect
import itertools
+import os
+import pathlib
+import tempfile
+import types
+from typing import cast, Optional, Union
-from typing import Union, Optional, cast
from .abc import ResourceReader, Traversable
Package = Union[types.ModuleType, str]
@@ -32,7 +32,7 @@ def get_resource_reader(package: types.ModuleType) ->
Optional[ResourceReader]:
# zipimport.zipimporter does not support weak references, resulting in a
# TypeError. That seems terrible.
spec = package.__spec__
- reader = getattr(spec.loader, 'get_resource_reader', None) # type:
ignore[union-attr]
+ reader = getattr(spec.loader, "get_resource_reader", None) # type:
ignore[union-attr]
if reader is None:
return None
return reader(spec.name) # type: ignore[union-attr]
@@ -50,7 +50,7 @@ def _(cand: str) -> types.ModuleType:
@resolve.register
def _(cand: None) -> types.ModuleType:
- return resolve(_infer_caller().f_globals['__name__'])
+ return resolve(_infer_caller().f_globals["__name__"])
def _infer_caller():
@@ -62,7 +62,7 @@ def is_this_file(frame_info):
return frame_info.filename == stack[0].filename
def is_wrapper(frame_info):
- return frame_info.function == 'wrapper'
+ return frame_info.function == "wrapper"
stack = inspect.stack()
not_this_file = itertools.filterfalse(is_this_file, stack)
@@ -87,7 +87,7 @@ def from_package(package: types.ModuleType):
@contextlib.contextmanager
def _tempfile(
reader,
- suffix='',
+ suffix="",
# gh-93353: Keep a reference to call os.remove() in late Python
# finalization.
*,
diff --git a/Lib/importlib/resources/_functional.py
b/Lib/importlib/resources/_functional.py
index f59416f2dd627d..b08a5c6efe22a2 100644
--- a/Lib/importlib/resources/_functional.py
+++ b/Lib/importlib/resources/_functional.py
@@ -2,8 +2,8 @@
import warnings
-from ._common import files, as_file
-
+from ._common import as_file, files
+from .abc import TraversalError
_MISSING = object()
@@ -42,7 +42,10 @@ def is_resource(anchor, *path_names):
Otherwise returns ``False``.
"""
- return _get_resource(anchor, path_names).is_file()
+ try:
+ return _get_resource(anchor, path_names).is_file()
+ except TraversalError:
+ return False
def contents(anchor, *path_names):
diff --git a/Lib/importlib/resources/abc.py b/Lib/importlib/resources/abc.py
index 6750a7aaf14aa9..64a6d843dce98e 100644
--- a/Lib/importlib/resources/abc.py
+++ b/Lib/importlib/resources/abc.py
@@ -1,12 +1,22 @@
import abc
-import io
import itertools
import os
import pathlib
-from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional
-from typing import runtime_checkable, Protocol
-from typing import Union
-
+from typing import (
+ Any,
+ BinaryIO,
+ Iterable,
+ Iterator,
+ Literal,
+ NoReturn,
+ Optional,
+ Protocol,
+ Text,
+ TextIO,
+ Union,
+ overload,
+ runtime_checkable,
+)
StrPath = Union[str, os.PathLike[str]]
@@ -82,11 +92,13 @@ def read_bytes(self) -> bytes:
with self.open('rb') as strm:
return strm.read()
- def read_text(self, encoding: Optional[str] = None) -> str:
+ def read_text(
+ self, encoding: Optional[str] = None, errors: Optional[str] = None
+ ) -> str:
"""
Read contents of self as text
"""
- with self.open(encoding=encoding) as strm:
+ with self.open(encoding=encoding, errors=errors) as strm:
return strm.read()
@abc.abstractmethod
@@ -132,8 +144,16 @@ def __truediv__(self, child: StrPath) -> "Traversable":
"""
return self.joinpath(child)
+ @overload
+ def open(self, mode: Literal['r'] = 'r', *args: Any, **kwargs: Any) ->
TextIO: ...
+
+ @overload
+ def open(self, mode: Literal['rb'], *args: Any, **kwargs: Any) ->
BinaryIO: ...
+
@abc.abstractmethod
- def open(self, mode='r', *args, **kwargs):
+ def open(
+ self, mode: str = 'r', *args: Any, **kwargs: Any
+ ) -> Union[TextIO, BinaryIO]:
"""
mode may be 'r' or 'rb' to open as text or binary. Return a handle
suitable for reading (same as pathlib.Path.open).
@@ -160,7 +180,7 @@ class TraversableResources(ResourceReader):
def files(self) -> "Traversable":
"""Return a Traversable object for the loaded package."""
- def open_resource(self, resource: StrPath) -> io.BufferedReader:
+ def open_resource(self, resource: StrPath) -> BinaryIO:
return self.files().joinpath(resource).open('rb')
def resource_path(self, resource: Any) -> NoReturn:
diff --git a/Lib/importlib/resources/readers.py
b/Lib/importlib/resources/readers.py
index 70fc7e2b9c0145..5d0ae46d672f53 100644
--- a/Lib/importlib/resources/readers.py
+++ b/Lib/importlib/resources/readers.py
@@ -3,15 +3,14 @@
import collections
import contextlib
import itertools
-import pathlib
import operator
+import pathlib
import re
import warnings
import zipfile
from collections.abc import Iterator
from . import abc
-
from ._itertools import only
diff --git a/Lib/test/test_importlib/resources/_path.py
b/Lib/test/test_importlib/resources/_path.py
index b144628cb73c77..0033983dc66286 100644
--- a/Lib/test/test_importlib/resources/_path.py
+++ b/Lib/test/test_importlib/resources/_path.py
@@ -1,10 +1,6 @@
-import pathlib
import functools
-
-from typing import Dict, Union
-from typing import runtime_checkable
-from typing import Protocol
-
+import pathlib
+from typing import Dict, Protocol, Union, runtime_checkable
####
# from jaraco.path 3.7.1
diff --git a/Lib/test/test_importlib/resources/test_compatibilty_files.py
b/Lib/test/test_importlib/resources/test_compatibilty_files.py
index bcf608d9e2cbdf..113f9ae6fdb20d 100644
--- a/Lib/test/test_importlib/resources/test_compatibilty_files.py
+++ b/Lib/test/test_importlib/resources/test_compatibilty_files.py
@@ -1,8 +1,6 @@
+import importlib.resources as resources
import io
import unittest
-
-from importlib import resources
-
from importlib.resources._adapters import (
CompatibilityFiles,
wrap_spec,
diff --git a/Lib/test/test_importlib/resources/test_contents.py
b/Lib/test/test_importlib/resources/test_contents.py
index 4e4e0e9c337f23..bdc158d85a239f 100644
--- a/Lib/test/test_importlib/resources/test_contents.py
+++ b/Lib/test/test_importlib/resources/test_contents.py
@@ -1,5 +1,5 @@
+import importlib.resources as resources
import unittest
-from importlib import resources
from . import util
diff --git a/Lib/test/test_importlib/resources/test_custom.py
b/Lib/test/test_importlib/resources/test_custom.py
index 640f90fc0dd91a..a7fc6bc35c5ece 100644
--- a/Lib/test/test_importlib/resources/test_custom.py
+++ b/Lib/test/test_importlib/resources/test_custom.py
@@ -1,12 +1,12 @@
-import unittest
import contextlib
+import importlib.resources as resources
import pathlib
+import unittest
+from importlib.resources import abc
+from importlib.resources.abc import ResourceReader, TraversableResources
from test.support import os_helper
-from importlib import resources
-from importlib.resources import abc
-from importlib.resources.abc import TraversableResources, ResourceReader
from . import util
diff --git a/Lib/test/test_importlib/resources/test_files.py
b/Lib/test/test_importlib/resources/test_files.py
index c935b1e10ac87c..abb5bf36e68c9f 100644
--- a/Lib/test/test_importlib/resources/test_files.py
+++ b/Lib/test/test_importlib/resources/test_files.py
@@ -1,15 +1,16 @@
+import contextlib
+import importlib
+import importlib.resources as resources
import pathlib
import py_compile
import textwrap
import unittest
import warnings
-import importlib
-import contextlib
-
-from importlib import resources
from importlib.resources.abc import Traversable
+
+from test.support import import_helper, os_helper
+
from . import util
-from test.support import os_helper, import_helper
@contextlib.contextmanager
@@ -62,7 +63,7 @@ def test_non_paths_in_dunder_path(self):
to cause the ``PathEntryFinder`` to be called when searching
for packages. In that case, resources should still be loadable.
"""
- import namespacedata01
+ import namespacedata01 # type: ignore[import-not-found]
namespacedata01.__path__.append(
'__editable__.sample_namespace-1.0.finder.__path_hook__'
@@ -153,7 +154,9 @@ def _compile_importlib(self):
sources = pathlib.Path(resources.__file__).parent
for source_path in sources.glob('**/*.py'):
- c_path =
c_resources.joinpath(source_path.relative_to(sources)).with_suffix('.pyc')
+ c_path =
c_resources.joinpath(source_path.relative_to(sources)).with_suffix(
+ '.pyc'
+ )
py_compile.compile(source_path, c_path)
self.fixtures.enter_context(import_helper.DirsOnSysPath(bin_site))
diff --git a/Lib/test/test_importlib/resources/test_functional.py
b/Lib/test/test_importlib/resources/test_functional.py
index e8d25fa4d9faf0..9e1a3a0e2767e3 100644
--- a/Lib/test/test_importlib/resources/test_functional.py
+++ b/Lib/test/test_importlib/resources/test_functional.py
@@ -1,17 +1,12 @@
-import unittest
-import os
import importlib
+import importlib.resources as resources
+import os
+import unittest
from test.support import warnings_helper
-from importlib import resources
-
from . import util
-# Since the functional API forwards to Traversable, we only test
-# filesystem resources here -- not zip files, namespace packages etc.
-# We do test for two kinds of Anchor, though.
-
class StringAnchorMixin:
anchor01 = 'data01'
@@ -28,7 +23,7 @@ def anchor02(self):
return importlib.import_module('data02')
-class FunctionalAPIBase(util.DiskSetup):
+class FunctionalAPIBase:
def setUp(self):
super().setUp()
self.load_fixture('data02')
@@ -43,6 +38,12 @@ def _gen_resourcetxt_path_parts(self):
with self.subTest(path_parts=path_parts):
yield path_parts
+ def assertEndsWith(self, string, suffix):
+ """Assert that `string` ends with `suffix`.
+
+ Used to ignore an architecture-specific UTF-16 byte-order mark."""
+ self.assertEqual(string[-len(suffix) :], suffix)
+
def test_read_text(self):
self.assertEqual(
resources.read_text(self.anchor01, 'utf-8.file'),
@@ -71,7 +72,7 @@ def test_read_text(self):
# fail with PermissionError rather than IsADirectoryError
with self.assertRaises(OSError):
resources.read_text(self.anchor01)
- with self.assertRaises(OSError):
+ with self.assertRaises((OSError, resources.abc.TraversalError)):
resources.read_text(self.anchor01, 'no-such-file')
with self.assertRaises(UnicodeDecodeError):
resources.read_text(self.anchor01, 'utf-16.file')
@@ -119,7 +120,7 @@ def test_open_text(self):
# fail with PermissionError rather than IsADirectoryError
with self.assertRaises(OSError):
resources.open_text(self.anchor01)
- with self.assertRaises(OSError):
+ with self.assertRaises((OSError, resources.abc.TraversalError)):
resources.open_text(self.anchor01, 'no-such-file')
with resources.open_text(self.anchor01, 'utf-16.file') as f:
with self.assertRaises(UnicodeDecodeError):
@@ -176,17 +177,23 @@ def test_contents(self):
set(c),
{'utf-8.file', 'utf-16.file', 'binary.file', 'subdirectory'},
)
- with self.assertRaises(OSError), warnings_helper.check_warnings((
- ".*contents.*",
- DeprecationWarning,
- )):
+ with (
+ self.assertRaises(OSError),
+ warnings_helper.check_warnings((
+ ".*contents.*",
+ DeprecationWarning,
+ )),
+ ):
list(resources.contents(self.anchor01, 'utf-8.file'))
for path_parts in self._gen_resourcetxt_path_parts():
- with self.assertRaises(OSError), warnings_helper.check_warnings((
- ".*contents.*",
- DeprecationWarning,
- )):
+ with (
+ self.assertRaises((OSError, resources.abc.TraversalError)),
+ warnings_helper.check_warnings((
+ ".*contents.*",
+ DeprecationWarning,
+ )),
+ ):
list(resources.contents(self.anchor01, *path_parts))
with warnings_helper.check_warnings((".*contents.*",
DeprecationWarning)):
c = resources.contents(self.anchor01, 'subdirectory')
@@ -233,17 +240,28 @@ def test_text_errors(self):
)
-class FunctionalAPITest_StringAnchor(
+class FunctionalAPITest_StringAnchor_Disk(
StringAnchorMixin,
FunctionalAPIBase,
+ util.DiskSetup,
unittest.TestCase,
):
pass
-class FunctionalAPITest_ModuleAnchor(
+class FunctionalAPITest_ModuleAnchor_Disk(
ModuleAnchorMixin,
FunctionalAPIBase,
+ util.DiskSetup,
+ unittest.TestCase,
+):
+ pass
+
+
+class FunctionalAPITest_StringAnchor_Memory(
+ StringAnchorMixin,
+ FunctionalAPIBase,
+ util.MemorySetup,
unittest.TestCase,
):
pass
diff --git a/Lib/test/test_importlib/resources/test_open.py
b/Lib/test/test_importlib/resources/test_open.py
index 8c00378ad3cc9c..b5a8949d52e532 100644
--- a/Lib/test/test_importlib/resources/test_open.py
+++ b/Lib/test/test_importlib/resources/test_open.py
@@ -1,6 +1,6 @@
+import importlib.resources as resources
import unittest
-from importlib import resources
from . import util
diff --git a/Lib/test/test_importlib/resources/test_path.py
b/Lib/test/test_importlib/resources/test_path.py
index 903911f57b3306..3d158d95b5023a 100644
--- a/Lib/test/test_importlib/resources/test_path.py
+++ b/Lib/test/test_importlib/resources/test_path.py
@@ -1,8 +1,8 @@
+import importlib.resources as resources
import io
import pathlib
import unittest
-from importlib import resources
from . import util
@@ -20,7 +20,7 @@ def test_reading(self):
target = resources.files(self.data) / 'utf-8.file'
with resources.as_file(target) as path:
self.assertIsInstance(path, pathlib.Path)
- self.assertEndsWith(path.name, "utf-8.file")
+ self.assertTrue(path.name.endswith("utf-8.file"), repr(path))
self.assertEqual('Hello, UTF-8 world!\n',
path.read_text(encoding='utf-8'))
diff --git a/Lib/test/test_importlib/resources/test_read.py
b/Lib/test/test_importlib/resources/test_read.py
index 59c237d964121e..cd1cc6dd86ff47 100644
--- a/Lib/test/test_importlib/resources/test_read.py
+++ b/Lib/test/test_importlib/resources/test_read.py
@@ -1,6 +1,6 @@
+import importlib.resources as resources
import unittest
-
-from importlib import import_module, resources
+from importlib import import_module
from . import util
diff --git a/Lib/test/test_importlib/resources/test_reader.py
b/Lib/test/test_importlib/resources/test_reader.py
index ed5693ab416798..cf23f38f3aaac5 100644
--- a/Lib/test/test_importlib/resources/test_reader.py
+++ b/Lib/test/test_importlib/resources/test_reader.py
@@ -1,9 +1,8 @@
import os.path
import pathlib
import unittest
-
from importlib import import_module
-from importlib.readers import MultiplexedPath, NamespaceReader
+from importlib.resources.readers import MultiplexedPath, NamespaceReader
from . import util
diff --git a/Lib/test/test_importlib/resources/test_resource.py
b/Lib/test/test_importlib/resources/test_resource.py
index fcede14b891a84..ef69cd049d9b4c 100644
--- a/Lib/test/test_importlib/resources/test_resource.py
+++ b/Lib/test/test_importlib/resources/test_resource.py
@@ -1,7 +1,8 @@
+import importlib.resources as resources
import unittest
+from importlib import import_module
from . import util
-from importlib import resources, import_module
class ResourceTests:
diff --git a/Lib/test/test_importlib/resources/test_util.py
b/Lib/test/test_importlib/resources/test_util.py
new file mode 100644
index 00000000000000..de304b6f3510a6
--- /dev/null
+++ b/Lib/test/test_importlib/resources/test_util.py
@@ -0,0 +1,29 @@
+import unittest
+
+from .util import MemorySetup, Traversable
+
+
+class TestMemoryTraversableImplementation(unittest.TestCase):
+ def test_concrete_methods_are_not_overridden(self):
+ """`MemoryTraversable` must not override `Traversable` concrete
methods.
+
+ This test is not an attempt to enforce a particular `Traversable`
protocol;
+ it merely catches changes in the `Traversable` abstract/concrete
methods
+ that have not been mirrored in the `MemoryTraversable` subclass.
+ """
+
+ traversable_concrete_methods = {
+ method
+ for method, value in Traversable.__dict__.items()
+ if callable(value) and method not in
Traversable.__abstractmethods__
+ }
+ memory_traversable_concrete_methods = {
+ method
+ for method, value in MemorySetup.MemoryTraversable.__dict__.items()
+ if callable(value) and not method.startswith("__")
+ }
+ overridden_methods = (
+ memory_traversable_concrete_methods & traversable_concrete_methods
+ )
+
+ assert not overridden_methods
diff --git a/Lib/test/test_importlib/resources/util.py
b/Lib/test/test_importlib/resources/util.py
index e2d995f596317d..d6a99289906e35 100644
--- a/Lib/test/test_importlib/resources/util.py
+++ b/Lib/test/test_importlib/resources/util.py
@@ -1,18 +1,18 @@
import abc
+import contextlib
+import functools
import importlib
import io
+import pathlib
import sys
import types
-import pathlib
-import contextlib
+from importlib.machinery import ModuleSpec
+from importlib.resources.abc import ResourceReader, Traversable,
TraversableResources
-from importlib.resources.abc import ResourceReader
from test.support import import_helper, os_helper
-from . import zip as zip_
-from . import _path
-
-from importlib.machinery import ModuleSpec
+from . import _path
+from . import zip as zip_
class Reader(ResourceReader):
@@ -202,5 +202,108 @@ def tree_on_path(self, spec):
self.fixtures.enter_context(import_helper.DirsOnSysPath(temp_dir))
+class MemorySetup(ModuleSetup):
+ """Support loading a module in memory."""
+
+ MODULE = 'data01'
+
+ def load_fixture(self, module):
+ self.fixtures.enter_context(self.augment_sys_metapath(module))
+ return importlib.import_module(module)
+
+ @contextlib.contextmanager
+ def augment_sys_metapath(self, module):
+ finder_instance = self.MemoryFinder(module)
+ sys.meta_path.append(finder_instance)
+ yield
+ sys.meta_path.remove(finder_instance)
+
+ class MemoryFinder(importlib.abc.MetaPathFinder):
+ def __init__(self, module):
+ self._module = module
+
+ def find_spec(self, fullname, path, target=None):
+ if fullname != self._module:
+ return None
+
+ return importlib.machinery.ModuleSpec(
+ name=fullname,
+ loader=MemorySetup.MemoryLoader(self._module),
+ is_package=True,
+ )
+
+ class MemoryLoader(importlib.abc.Loader):
+ def __init__(self, module):
+ self._module = module
+
+ def exec_module(self, module):
+ pass
+
+ def get_resource_reader(self, fullname):
+ return MemorySetup.MemoryTraversableResources(self._module,
fullname)
+
+ class MemoryTraversableResources(TraversableResources):
+ def __init__(self, module, fullname):
+ self._module = module
+ self._fullname = fullname
+
+ def files(self):
+ return MemorySetup.MemoryTraversable(self._module, self._fullname)
+
+ class MemoryTraversable(Traversable):
+ """Implement only the abstract methods of `Traversable`.
+
+ Besides `.__init__()`, no other methods may be implemented or
overridden.
+ This is critical for validating the concrete `Traversable`
implementations.
+ """
+
+ def __init__(self, module, fullname):
+ self._module = module
+ self._fullname = fullname
+
+ def _resolve(self):
+ """
+ Fully traverse the `fixtures` dictionary.
+
+ This should be wrapped in a `try/except KeyError`
+ but it is not currently needed and lowers the code coverage
numbers.
+ """
+ path = pathlib.PurePosixPath(self._fullname)
+ return functools.reduce(lambda d, p: d[p], path.parts, fixtures)
+
+ def iterdir(self):
+ directory = self._resolve()
+ if not isinstance(directory, dict):
+ # Filesystem openers raise OSError, and that exception is
mirrored here.
+ raise OSError(f"{self._fullname} is not a directory")
+ for path in directory:
+ yield MemorySetup.MemoryTraversable(
+ self._module, f"{self._fullname}/{path}"
+ )
+
+ def is_dir(self) -> bool:
+ return isinstance(self._resolve(), dict)
+
+ def is_file(self) -> bool:
+ return not self.is_dir()
+
+ def open(self, mode='r', encoding=None, errors=None, *_, **__):
+ contents = self._resolve()
+ if isinstance(contents, dict):
+ # Filesystem openers raise OSError when attempting to open a
directory,
+ # and that exception is mirrored here.
+ raise OSError(f"{self._fullname} is a directory")
+ if isinstance(contents, str):
+ contents = contents.encode("utf-8")
+ result = io.BytesIO(contents)
+ if "b" in mode:
+ return result
+ return io.TextIOWrapper(result, encoding=encoding, errors=errors)
+
+ @property
+ def name(self):
+ return pathlib.PurePosixPath(self._fullname).name
+
+
class CommonTests(DiskSetup, CommonTestsBase):
pass
diff --git
a/Misc/NEWS.d/next/Library/2026-04-11-17-28-06.gh-issue-127012.h3rLYS.rst
b/Misc/NEWS.d/next/Library/2026-04-11-17-28-06.gh-issue-127012.h3rLYS.rst
new file mode 100644
index 00000000000000..eafefb8a6c0cf5
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-04-11-17-28-06.gh-issue-127012.h3rLYS.rst
@@ -0,0 +1,2 @@
+``importlib.abc.Traversable.read_text`` now allows/solicits an
+``errors`` parameter.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]