You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Recommend importlib.abc.Traversable users to implement __fspath__ #88366
The files()/Traversable api is meant to replace ResourceReader api, but it falls short in one dimension, tying the abstraction to a file system path.
I propose to document that Traversable users *should* implement __fspath__ if the Traversable represents a file-system path.
This will essentially make os.fspath(importlib.resources.files(module) / resource) the equivalent of importlib.resources.path(module, resource), for which currently there is no replacement using files().
The problem with the __fspath__ protocol is that it assumes a file system. The importlib.resources and Traversable protocols are trying to provide a lower-level interface that doesn't assume a file system. Fortunately, there already exists a helper function as_file (https://docs.python.org/3/library/importlib.html#importlib.resources.as_file) that's meant to provide the user experience that path once provided, but for Traversable files.
That's why I said it should be optional. If the object can implement it, it should. pathlib.Path certainly can, zipfile.Path can't, and that's alright.
If the Traversable does not represent a path on the file-system, it does not make sense to implement __fspath__, so it shouldn't.
I think as_file should learn to behave like path, if __fspath__ is available, it will use that path, otherwise it will do what it does currently, which is creating a temporary path and writing the resource contents there.
I think as_file should learn to behave like path, if __fspath__ is available, it will use that path.
Oh, that's an interesting idea. And that's effectively what happens here, where as_file returns a native Path object if that's what it encounters. Maybe another dispatcher could accept os.PathLike and in that case, return pathlib.Path(os.fspath(path)).
Only thing is, that code would serve no purpose until there existed at least one other resource provider besides FileReader that has file-system-local resources. I'm not aware of any such provider.
CompatibilityFiles would use this. We could add a dispatch for that too, but that's 2 different cases now and os.PathLike would be able to handle both.
CompatibilityFiles would use this. We could add a dispatch for that too, but that's 2 different cases now and os.PathLike would be able to handle both.
I'm not sure I understand the two cases. Perhaps you could illustrate how this would work within CompatibilityFiles?
CompatibilityFiles is an abstraction over ResourceReader, it implements a Traversable interface using ResourceReader.contents, ResourceReader.resource_path, etc. In this case, the resource file can exist on the filesystem, but we cannot simply return a pathlib.Path of the root of the package in files(), as the reader might customize the available resources.
Currently, if I need to access a resource as a file on the filesystem, I need to use as_file or something similar, which will write the resource contents to a temporary file. This isn't optimal, so to avoid it, we could register another dispatch method in as_file to special-case CompatibilityFiles, like we do for pathlib.Path:
But I feel like a better solution would be to have the Traversable in question, which in this example is CompatibilityFiles, implement __fspath__ if it is representing a path on the filesystem. This has two benefits:
CompatibilityFiles would provide an interface to get the resource path (independently of as_file)
We could have as_file implement a dispatch for os.PathLike, avoiding the situation described above not only for CompatibilityFiles, but for any Traversable that provides the filesystem path via __fspath__
Activity
FFY00 commentedon May 21, 2021
The files()/Traversable api is meant to replace ResourceReader api, but it falls short in one dimension, tying the abstraction to a file system path.
I propose to document that Traversable users *should* implement __fspath__ if the Traversable represents a file-system path.
This will essentially make os.fspath(importlib.resources.files(module) / resource) the equivalent of importlib.resources.path(module, resource), for which currently there is no replacement using files().
FFY00 commentedon May 21, 2021
I am gonna wait until/if #70460 (bpo-44196) gets merged because this would conflict with that.
jaraco commentedon May 23, 2021
The problem with the
__fspath__protocol is that it assumes a file system. Theimportlib.resourcesandTraversableprotocols are trying to provide a lower-level interface that doesn't assume a file system. Fortunately, there already exists a helper functionas_file(https://docs.python.org/3/library/importlib.html#importlib.resources.as_file) that's meant to provide the user experience thatpathonce provided, but for Traversable files.Does that satisfy the need you've identified?
FFY00 commentedon May 23, 2021
That's why I said it should be optional. If the object can implement it, it should.
pathlib.Pathcertainly can,zipfile.Pathcan't, and that's alright.If the Traversable does not represent a path on the file-system, it does not make sense to implement
__fspath__, so it shouldn't.I think
as_fileshould learn to behave likepath, if__fspath__is available, it will use that path, otherwise it will do what it does currently, which is creating a temporary path and writing the resource contents there.jaraco commentedon May 23, 2021
Oh, that's an interesting idea. And that's effectively what happens here, where
as_filereturns a native Path object if that's what it encounters. Maybe another dispatcher could acceptos.PathLikeand in that case, returnpathlib.Path(os.fspath(path)).Only thing is, that code would serve no purpose until there existed at least one other resource provider besides FileReader that has file-system-local resources. I'm not aware of any such provider.
FFY00 commentedon May 23, 2021
CompatibilityFiles would use this. We could add a dispatch for that too, but that's 2 different cases now and
os.PathLikewould be able to handle both.moomoohk commentedon Sep 16, 2022
jaraco commentedon Mar 21, 2024
I'm not sure I understand the two cases. Perhaps you could illustrate how this would work within CompatibilityFiles?
FFY00 commentedon Apr 9, 2024
CompatibilityFilesis an abstraction overResourceReader, it implements aTraversableinterface usingResourceReader.contents,ResourceReader.resource_path, etc. In this case, the resource file can exist on the filesystem, but we cannot simply return apathlib.Pathof the root of the package infiles(), as the reader might customize the available resources.Currently, if I need to access a resource as a file on the filesystem, I need to use
as_fileor something similar, which will write the resource contents to a temporary file. This isn't optimal, so to avoid it, we could register another dispatch method inas_fileto special-caseCompatibilityFiles, like we do forpathlib.Path:https://github.com/python/importlib_resources/blob/6fa0a7aef25178430816c4459524a4945bfb7ee1/importlib_resources/_common.py#L173-L179
But I feel like a better solution would be to have the
Traversablein question, which in this example isCompatibilityFiles, implement__fspath__if it is representing a path on the filesystem. This has two benefits:CompatibilityFileswould provide an interface to get the resource path (independently ofas_file)as_fileimplement a dispatch foros.PathLike, avoiding the situation described above not only forCompatibilityFiles, but for anyTraversablethat provides the filesystem path via__fspath__