Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/importlib_resources/abc.py: 65%

65 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:51 +0000

1import abc 

2import io 

3import itertools 

4import pathlib 

5from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional 

6 

7from ._compat import runtime_checkable, Protocol, StrPath 

8 

9 

10__all__ = ["ResourceReader", "Traversable", "TraversableResources"] 

11 

12 

13class ResourceReader(metaclass=abc.ABCMeta): 

14 """Abstract base class for loaders to provide resource reading support.""" 

15 

16 @abc.abstractmethod 

17 def open_resource(self, resource: Text) -> BinaryIO: 

18 """Return an opened, file-like object for binary reading. 

19 

20 The 'resource' argument is expected to represent only a file name. 

21 If the resource cannot be found, FileNotFoundError is raised. 

22 """ 

23 # This deliberately raises FileNotFoundError instead of 

24 # NotImplementedError so that if this method is accidentally called, 

25 # it'll still do the right thing. 

26 raise FileNotFoundError 

27 

28 @abc.abstractmethod 

29 def resource_path(self, resource: Text) -> Text: 

30 """Return the file system path to the specified resource. 

31 

32 The 'resource' argument is expected to represent only a file name. 

33 If the resource does not exist on the file system, raise 

34 FileNotFoundError. 

35 """ 

36 # This deliberately raises FileNotFoundError instead of 

37 # NotImplementedError so that if this method is accidentally called, 

38 # it'll still do the right thing. 

39 raise FileNotFoundError 

40 

41 @abc.abstractmethod 

42 def is_resource(self, path: Text) -> bool: 

43 """Return True if the named 'path' is a resource. 

44 

45 Files are resources, directories are not. 

46 """ 

47 raise FileNotFoundError 

48 

49 @abc.abstractmethod 

50 def contents(self) -> Iterable[str]: 

51 """Return an iterable of entries in `package`.""" 

52 raise FileNotFoundError 

53 

54 

55class TraversalError(Exception): 

56 pass 

57 

58 

59@runtime_checkable 

60class Traversable(Protocol): 

61 """ 

62 An object with a subset of pathlib.Path methods suitable for 

63 traversing directories and opening files. 

64 

65 Any exceptions that occur when accessing the backing resource 

66 may propagate unaltered. 

67 """ 

68 

69 @abc.abstractmethod 

70 def iterdir(self) -> Iterator["Traversable"]: 

71 """ 

72 Yield Traversable objects in self 

73 """ 

74 

75 def read_bytes(self) -> bytes: 

76 """ 

77 Read contents of self as bytes 

78 """ 

79 with self.open('rb') as strm: 

80 return strm.read() 

81 

82 def read_text(self, encoding: Optional[str] = None) -> str: 

83 """ 

84 Read contents of self as text 

85 """ 

86 with self.open(encoding=encoding) as strm: 

87 return strm.read() 

88 

89 @abc.abstractmethod 

90 def is_dir(self) -> bool: 

91 """ 

92 Return True if self is a directory 

93 """ 

94 

95 @abc.abstractmethod 

96 def is_file(self) -> bool: 

97 """ 

98 Return True if self is a file 

99 """ 

100 

101 def joinpath(self, *descendants: StrPath) -> "Traversable": 

102 """ 

103 Return Traversable resolved with any descendants applied. 

104 

105 Each descendant should be a path segment relative to self 

106 and each may contain multiple levels separated by 

107 ``posixpath.sep`` (``/``). 

108 """ 

109 if not descendants: 

110 return self 

111 names = itertools.chain.from_iterable( 

112 path.parts for path in map(pathlib.PurePosixPath, descendants) 

113 ) 

114 target = next(names) 

115 matches = ( 

116 traversable for traversable in self.iterdir() if traversable.name == target 

117 ) 

118 try: 

119 match = next(matches) 

120 except StopIteration: 

121 raise TraversalError( 

122 "Target not found during traversal.", target, list(names) 

123 ) 

124 return match.joinpath(*names) 

125 

126 def __truediv__(self, child: StrPath) -> "Traversable": 

127 """ 

128 Return Traversable child in self 

129 """ 

130 return self.joinpath(child) 

131 

132 @abc.abstractmethod 

133 def open(self, mode='r', *args, **kwargs): 

134 """ 

135 mode may be 'r' or 'rb' to open as text or binary. Return a handle 

136 suitable for reading (same as pathlib.Path.open). 

137 

138 When opening as text, accepts encoding parameters such as those 

139 accepted by io.TextIOWrapper. 

140 """ 

141 

142 @property 

143 @abc.abstractmethod 

144 def name(self) -> str: 

145 """ 

146 The base name of this object without any parent references. 

147 """ 

148 

149 

150class TraversableResources(ResourceReader): 

151 """ 

152 The required interface for providing traversable 

153 resources. 

154 """ 

155 

156 @abc.abstractmethod 

157 def files(self) -> "Traversable": 

158 """Return a Traversable object for the loaded package.""" 

159 

160 def open_resource(self, resource: StrPath) -> io.BufferedReader: 

161 return self.files().joinpath(resource).open('rb') 

162 

163 def resource_path(self, resource: Any) -> NoReturn: 

164 raise FileNotFoundError(resource) 

165 

166 def is_resource(self, path: StrPath) -> bool: 

167 return self.files().joinpath(path).is_file() 

168 

169 def contents(self) -> Iterator[str]: 

170 return (item.name for item in self.files().iterdir())