Skip to content
Snippets Groups Projects
Unverified Commit 76596815 authored by Jason R. Coombs's avatar Jason R. Coombs Committed by GitHub
Browse files

gh-91298: Refine traversable (apply changes from importlib_resources 5.7.1) (#91623)

* bpo-47142: Refine traversable (apply changes from importlib_resources 5.7.1)

* Replace changelog referencing github issue.
parent 67712e71
Branches
Tags
No related merge requests found
import abc
from typing import BinaryIO, Iterable, Text
import io
import os
from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional
from typing import runtime_checkable, Protocol
from typing import Union
StrPath = Union[str, os.PathLike[str]]
__all__ = ["ResourceReader", "Traversable", "TraversableResources"]
class ResourceReader(metaclass=abc.ABCMeta):
......@@ -50,22 +58,25 @@ class Traversable(Protocol):
"""
An object with a subset of pathlib.Path methods suitable for
traversing directories and opening files.
Any exceptions that occur when accessing the backing resource
may propagate unaltered.
"""
@abc.abstractmethod
def iterdir(self):
def iterdir(self) -> Iterator["Traversable"]:
"""
Yield Traversable objects in self
"""
def read_bytes(self):
def read_bytes(self) -> bytes:
"""
Read contents of self as bytes
"""
with self.open('rb') as strm:
return strm.read()
def read_text(self, encoding=None):
def read_text(self, encoding: Optional[str] = None) -> str:
"""
Read contents of self as text
"""
......@@ -85,12 +96,16 @@ def is_file(self) -> bool:
"""
@abc.abstractmethod
def joinpath(self, child):
def joinpath(self, *descendants: StrPath) -> "Traversable":
"""
Return Traversable child in self
Return Traversable resolved with any descendants applied.
Each descendant should be a path segment relative to self
and each may contain multiple levels separated by
``posixpath.sep`` (``/``).
"""
def __truediv__(self, child):
def __truediv__(self, child: StrPath) -> "Traversable":
"""
Return Traversable child in self
"""
......@@ -120,17 +135,17 @@ class TraversableResources(ResourceReader):
"""
@abc.abstractmethod
def files(self):
def files(self) -> "Traversable":
"""Return a Traversable object for the loaded package."""
def open_resource(self, resource):
def open_resource(self, resource: StrPath) -> io.BufferedReader:
return self.files().joinpath(resource).open('rb')
def resource_path(self, resource):
def resource_path(self, resource: Any) -> NoReturn:
raise FileNotFoundError(resource)
def is_resource(self, path):
def is_resource(self, path: StrPath) -> bool:
return self.files().joinpath(path).is_file()
def contents(self):
def contents(self) -> Iterator[str]:
return (item.name for item in self.files().iterdir())
......@@ -99,10 +99,19 @@ def iterdir(self):
def open(self, *args, **kwargs):
raise IsADirectoryError()
def joinpath(self, name):
@staticmethod
def _flatten(compound_names):
for name in compound_names:
yield from name.split('/')
def joinpath(self, *descendants):
if not descendants:
return self
names = self._flatten(descendants)
target = next(names)
return next(
traversable for traversable in self.iterdir() if traversable.name == name
)
traversable for traversable in self.iterdir() if traversable.name == target
).joinpath(*names)
class TraversableReader(TraversableResources, SimpleReader):
......
......@@ -42,7 +42,7 @@ def generate(suffix):
def walk(datapath):
for dirpath, dirnames, filenames in os.walk(datapath):
with contextlib.suppress(KeyError):
with contextlib.suppress(ValueError):
dirnames.remove('__pycache__')
for filename in filenames:
res = pathlib.Path(dirpath) / filename
......
In ``importlib.resources.abc``, refined the documentation of the Traversable
Protocol, applying changes from importlib_resources 5.7.1.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment