python (3.11.7)
       1  import abc
       2  import io
       3  import os
       4  from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional
       5  from typing import runtime_checkable, Protocol
       6  from typing import Union
       7  
       8  
       9  StrPath = Union[str, os.PathLike[str]]
      10  
      11  __all__ = ["ResourceReader", "Traversable", "TraversableResources"]
      12  
      13  
      14  class ESC[4;38;5;81mResourceReader(metaclass=ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mABCMeta):
      15      """Abstract base class for loaders to provide resource reading support."""
      16  
      17      @abc.abstractmethod
      18      def open_resource(self, resource: Text) -> BinaryIO:
      19          """Return an opened, file-like object for binary reading.
      20  
      21          The 'resource' argument is expected to represent only a file name.
      22          If the resource cannot be found, FileNotFoundError is raised.
      23          """
      24          # This deliberately raises FileNotFoundError instead of
      25          # NotImplementedError so that if this method is accidentally called,
      26          # it'll still do the right thing.
      27          raise FileNotFoundError
      28  
      29      @abc.abstractmethod
      30      def resource_path(self, resource: Text) -> Text:
      31          """Return the file system path to the specified resource.
      32  
      33          The 'resource' argument is expected to represent only a file name.
      34          If the resource does not exist on the file system, raise
      35          FileNotFoundError.
      36          """
      37          # This deliberately raises FileNotFoundError instead of
      38          # NotImplementedError so that if this method is accidentally called,
      39          # it'll still do the right thing.
      40          raise FileNotFoundError
      41  
      42      @abc.abstractmethod
      43      def is_resource(self, path: Text) -> bool:
      44          """Return True if the named 'path' is a resource.
      45  
      46          Files are resources, directories are not.
      47          """
      48          raise FileNotFoundError
      49  
      50      @abc.abstractmethod
      51      def contents(self) -> Iterable[str]:
      52          """Return an iterable of entries in `package`."""
      53          raise FileNotFoundError
      54  
      55  
      56  @runtime_checkable
      57  class ESC[4;38;5;81mTraversable(ESC[4;38;5;149mProtocol):
      58      """
      59      An object with a subset of pathlib.Path methods suitable for
      60      traversing directories and opening files.
      61  
      62      Any exceptions that occur when accessing the backing resource
      63      may propagate unaltered.
      64      """
      65  
      66      @abc.abstractmethod
      67      def iterdir(self) -> Iterator["Traversable"]:
      68          """
      69          Yield Traversable objects in self
      70          """
      71  
      72      def read_bytes(self) -> bytes:
      73          """
      74          Read contents of self as bytes
      75          """
      76          with self.open('rb') as strm:
      77              return strm.read()
      78  
      79      def read_text(self, encoding: Optional[str] = None) -> str:
      80          """
      81          Read contents of self as text
      82          """
      83          with self.open(encoding=encoding) as strm:
      84              return strm.read()
      85  
      86      @abc.abstractmethod
      87      def is_dir(self) -> bool:
      88          """
      89          Return True if self is a directory
      90          """
      91  
      92      @abc.abstractmethod
      93      def is_file(self) -> bool:
      94          """
      95          Return True if self is a file
      96          """
      97  
      98      @abc.abstractmethod
      99      def joinpath(self, *descendants: StrPath) -> "Traversable":
     100          """
     101          Return Traversable resolved with any descendants applied.
     102  
     103          Each descendant should be a path segment relative to self
     104          and each may contain multiple levels separated by
     105          ``posixpath.sep`` (``/``).
     106          """
     107  
     108      def __truediv__(self, child: StrPath) -> "Traversable":
     109          """
     110          Return Traversable child in self
     111          """
     112          return self.joinpath(child)
     113  
     114      @abc.abstractmethod
     115      def open(self, mode='r', *args, **kwargs):
     116          """
     117          mode may be 'r' or 'rb' to open as text or binary. Return a handle
     118          suitable for reading (same as pathlib.Path.open).
     119  
     120          When opening as text, accepts encoding parameters such as those
     121          accepted by io.TextIOWrapper.
     122          """
     123  
     124      @abc.abstractproperty
     125      def name(self) -> str:
     126          """
     127          The base name of this object without any parent references.
     128          """
     129  
     130  
     131  class ESC[4;38;5;81mTraversableResources(ESC[4;38;5;149mResourceReader):
     132      """
     133      The required interface for providing traversable
     134      resources.
     135      """
     136  
     137      @abc.abstractmethod
     138      def files(self) -> "Traversable":
     139          """Return a Traversable object for the loaded package."""
     140  
     141      def open_resource(self, resource: StrPath) -> io.BufferedReader:
     142          return self.files().joinpath(resource).open('rb')
     143  
     144      def resource_path(self, resource: Any) -> NoReturn:
     145          raise FileNotFoundError(resource)
     146  
     147      def is_resource(self, path: StrPath) -> bool:
     148          return self.files().joinpath(path).is_file()
     149  
     150      def contents(self) -> Iterator[str]:
     151          return (item.name for item in self.files().iterdir())