Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32k
Description
Bug report
Bug description:
I'm writing a custom importer and discovered that the function signature forimportlib.abc.Traversable.read_text()
is incompatible with the usage inimportlib.resources._functional.read_text()
, specifically on Python 3.13.
importlib.abc.Traversable.read_text()
is a concrete method; its implementation calls the.open()
method, which is an abstract method that must be implemented. The expectation is therefore that implementing.open()
in a Traversable subclass is sufficient for.read_text()
to work.Note below that the
.read_text()
method is not marked as abstract, and includes only one parameter:encoding
:cpython/Lib/importlib/resources/abc.py
Lines 84 to 90 in30aeb00
defread_text(self,encoding:Optional[str]=None)->str: """ Read contents of self as text """ withself.open(encoding=encoding)asstrm: returnstrm.read() Application code that attempts to read a package resource, like
importlib.resources.read_text(module, "resource.txt")
ultimately leads to a call toimportlib.resources._functional.read_text()
, which attempts to call the.read_text()
method of a Traversable subclass, but includes anerrors
parameter that doesn't exist in Traversable's default concrete method:cpython/Lib/importlib/resources/_functional.py
Lines 28 to 32 in30aeb00
defread_text(anchor,*path_names,encoding=_MISSING,errors='strict'): """Read and return contents of *resource* within *package* as str.""" encoding=_get_encoding_arg(path_names,encoding) resource=_get_resource(anchor,path_names) returnresource.read_text(encoding=encoding,errors=errors) Consequently, it appears to be necessary for all Traversable subclasses to not only re-implement its concrete
.read_text()
method, but also to override its signature.
I think that the Traversable.read_text()
method signature, and the call site inimportlib.resources._functional.read_text()
, need to align with each other.
I'd like to submit a PR for this! However, I would like confirmation that anerrors
parameter should be added to theTraversable.read_text()
method.
Note that adding anerrors
parameter was previously discussed in#88368.
Demonstration of TypeError bug
importioimportsysimporttypingimportpathlibimporttypesimportimportlib.abcimportimportlib.machineryimportimportlib.metadataimportimportlib.resources.abcclassExampleFinder(importlib.abc.MetaPathFinder):deffind_spec(self,fullname:str,path:typing.Sequence[str]|None,target:types.ModuleType|None=None, )->importlib.machinery.ModuleSpec|None:iffullname!="demonstrate_error":returnNoneprint(f"ExampleFinder.find_spec('{fullname}')")spec=importlib.machinery.ModuleSpec(name=fullname,loader=ExampleLoader(),is_package=True, )returnspecsys.meta_path.append(ExampleFinder())classExampleLoader(importlib.abc.Loader):defexec_module(self,module:types.ModuleType)->None:print(f"ExampleLoader.exec_module({module})")exec("",module.__dict__)defget_resource_reader(self,fullname:str)->"ExampleTraversableResources":print(f"ExampleLoader.get_resource_reader('{fullname}')")returnExampleTraversableResources(fullname)classExampleTraversableResources(importlib.resources.abc.TraversableResources):def__init__(self,fullname:str)->None:self.fullname=fullnamedeffiles(self)->"ExampleTraversable":print("ExampleTraversableResources.files()")returnExampleTraversable(self.fullname)# ----------------------------------------------------------------------------# ExampleTraversable implements all five of the Traversable abstract methods.# Specifically, it is expected that implementing `.open()` will be sufficient,# but this will not be the case.#classExampleTraversable(importlib.resources.abc.Traversable):def__init__(self,path:str):self._path=pathdefiterdir(self)->typing.Iterator["ExampleTraversable"]:yieldExampleTraversable("resource.txt")defis_dir(self)->bool:returnFalsedefis_file(self)->bool:returnTruedefopen(self,mode='r',*args,**kwargs)->typing.IO[typing.AnyStr]:returnio.StringIO("Nice! The call to .read_text() succeeded!")# Uncomment this `.read_text()` method to make `.read_text()` calls work.# It overrides the `Traversable.read_text()` signature.## def read_text(self, encoding: str | None, errors: str | None) -> str:# print(f"ExampleTraversable.read_text('{encoding}', '{errors}')")# return str(super().read_text(encoding))@propertydefname(self)->str:returnpathlib.PurePosixPath(self._path).name# -------------------------------------------------------------------------------# Everything above allows us to import this hard-coded module# and demonstrate a TypeError lurking in the Traversable.read_text() signature.#importdemonstrate_error# The next line will raise a TypeError.# `importlib/resources/_functional.py:read_text()` calls `Traversable.read_text()`# with an `errors` argument that is not supported by the default concrete method.print(importlib.resources.read_text(demonstrate_error,"resource.txt"))
CPython versions tested on:
3.13
Operating systems tested on:
Linux