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