11import abc
2- from typing import BinaryIO , Iterable , Text
2+ import io
3+ import os
4+ from typing import Any , BinaryIO , Iterable , Iterator , NoReturn , Text , Optional
35from 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" ]
412
513
614class ResourceReader (metaclass = abc .ABCMeta ):
@@ -50,22 +58,25 @@ class Traversable(Protocol):
5058 """
5159 An object with a subset of pathlib.Path methods suitable for
5260 traversing directories and opening files.
61+
62+ Any exceptions that occur when accessing the backing resource
63+ may propagate unaltered.
5364 """
5465
5566 @abc .abstractmethod
56- def iterdir (self ):
67+ def iterdir (self ) -> Iterator [ "Traversable" ] :
5768 """
5869 Yield Traversable objects in self
5970 """
6071
61- def read_bytes (self ):
72+ def read_bytes (self ) -> bytes :
6273 """
6374 Read contents of self as bytes
6475 """
6576 with self .open ('rb' ) as strm :
6677 return strm .read ()
6778
68- def read_text (self , encoding = None ):
79+ def read_text (self , encoding : Optional [ str ] = None ) -> str :
6980 """
7081 Read contents of self as text
7182 """
@@ -85,12 +96,16 @@ def is_file(self) -> bool:
8596 """
8697
8798 @abc .abstractmethod
88- def joinpath (self , child ) :
99+ def joinpath (self , * descendants : StrPath ) -> "Traversable" :
89100 """
90- Return Traversable child in self
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`` (``/``).
91106 """
92107
93- def __truediv__ (self , child ) :
108+ def __truediv__ (self , child : StrPath ) -> "Traversable" :
94109 """
95110 Return Traversable child in self
96111 """
@@ -120,17 +135,17 @@ class TraversableResources(ResourceReader):
120135 """
121136
122137 @abc .abstractmethod
123- def files (self ):
138+ def files (self ) -> "Traversable" :
124139 """Return a Traversable object for the loaded package."""
125140
126- def open_resource (self , resource ) :
141+ def open_resource (self , resource : StrPath ) -> io . BufferedReader :
127142 return self .files ().joinpath (resource ).open ('rb' )
128143
129- def resource_path (self , resource ) :
144+ def resource_path (self , resource : Any ) -> NoReturn :
130145 raise FileNotFoundError (resource )
131146
132- def is_resource (self , path ) :
147+ def is_resource (self , path : StrPath ) -> bool :
133148 return self .files ().joinpath (path ).is_file ()
134149
135- def contents (self ):
150+ def contents (self ) -> Iterator [ str ] :
136151 return (item .name for item in self .files ().iterdir ())
0 commit comments