aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/importlib/resources/abc.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/importlib/resources/abc.py')
-rw-r--r--Lib/importlib/resources/abc.py23
1 files changed, 22 insertions, 1 deletions
diff --git a/Lib/importlib/resources/abc.py b/Lib/importlib/resources/abc.py
index 0b7bfdc4158..67c78c07856 100644
--- a/Lib/importlib/resources/abc.py
+++ b/Lib/importlib/resources/abc.py
@@ -1,6 +1,8 @@
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
@@ -53,6 +55,10 @@ class ResourceReader(metaclass=abc.ABCMeta):
raise FileNotFoundError
+class TraversalError(Exception):
+ pass
+
+
@runtime_checkable
class Traversable(Protocol):
"""
@@ -95,7 +101,6 @@ class Traversable(Protocol):
Return True if self is a file
"""
- @abc.abstractmethod
def joinpath(self, *descendants: StrPath) -> "Traversable":
"""
Return Traversable resolved with any descendants applied.
@@ -104,6 +109,22 @@ class Traversable(Protocol):
and each may contain multiple levels separated by
``posixpath.sep`` (``/``).
"""
+ if not descendants:
+ return self
+ names = itertools.chain.from_iterable(
+ path.parts for path in map(pathlib.PurePosixPath, descendants)
+ )
+ target = next(names)
+ matches = (
+ traversable for traversable in self.iterdir() if traversable.name == target
+ )
+ try:
+ match = next(matches)
+ except StopIteration:
+ raise TraversalError(
+ "Target not found during traversal.", target, list(names)
+ )
+ return match.joinpath(*names)
def __truediv__(self, child: StrPath) -> "Traversable":
"""