Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pathspec/pathspec.py: 41%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

91 statements  

1""" 

2This module provides an object oriented interface for pattern matching of files. 

3""" 

4 

5from collections.abc import ( 

6 Collection as CollectionType) 

7from itertools import ( 

8 zip_longest) 

9from typing import ( 

10 AnyStr, 

11 Callable, # Replaced by `collections.abc.Callable` in 3.9. 

12 Collection, # Replaced by `collections.abc.Collection` in 3.9. 

13 Iterable, # Replaced by `collections.abc.Iterable` in 3.9. 

14 Iterator, # Replaced by `collections.abc.Iterator` in 3.9. 

15 Optional, # Replaced by `X | None` in 3.10. 

16 Type, # Replaced by `type` in 3.9. 

17 TypeVar, 

18 Union) # Replaced by `X | Y` in 3.10. 

19 

20from . import util 

21from .pattern import ( 

22 Pattern) 

23from .util import ( 

24 CheckResult, 

25 StrPath, 

26 TStrPath, 

27 TreeEntry, 

28 _filter_check_patterns, 

29 _is_iterable, 

30 normalize_file) 

31 

32Self = TypeVar("Self", bound="PathSpec") 

33""" 

34:class:`PathSpec` self type hint to support Python v<3.11 using PEP 673 

35recommendation. 

36""" 

37 

38 

39class PathSpec(object): 

40 """ 

41 The :class:`PathSpec` class is a wrapper around a list of compiled 

42 :class:`.Pattern` instances. 

43 """ 

44 

45 def __init__(self, patterns: Iterable[Pattern]) -> None: 

46 """ 

47 Initializes the :class:`PathSpec` instance. 

48 

49 *patterns* (:class:`~collections.abc.Collection` or :class:`~collections.abc.Iterable`) 

50 yields each compiled pattern (:class:`.Pattern`). 

51 """ 

52 if not isinstance(patterns, CollectionType): 

53 patterns = list(patterns) 

54 

55 self.patterns: Collection[Pattern] = patterns 

56 """ 

57 *patterns* (:class:`~collections.abc.Collection` of :class:`.Pattern`) 

58 contains the compiled patterns. 

59 """ 

60 

61 def __eq__(self, other: object) -> bool: 

62 """ 

63 Tests the equality of this path-spec with *other* (:class:`PathSpec`) 

64 by comparing their :attr:`~PathSpec.patterns` attributes. 

65 """ 

66 if isinstance(other, PathSpec): 

67 paired_patterns = zip_longest(self.patterns, other.patterns) 

68 return all(a == b for a, b in paired_patterns) 

69 else: 

70 return NotImplemented 

71 

72 def __len__(self) -> int: 

73 """ 

74 Returns the number of compiled patterns this path-spec contains 

75 (:class:`int`). 

76 """ 

77 return len(self.patterns) 

78 

79 def __add__(self: Self, other: "PathSpec") -> Self: 

80 """ 

81 Combines the :attr:`Pathspec.patterns` patterns from two 

82 :class:`PathSpec` instances. 

83 """ 

84 if isinstance(other, PathSpec): 

85 return self.__class__(self.patterns + other.patterns) 

86 else: 

87 return NotImplemented 

88 

89 def __iadd__(self: Self, other: "PathSpec") -> Self: 

90 """ 

91 Adds the :attr:`Pathspec.patterns` patterns from one :class:`PathSpec` 

92 instance to this instance. 

93 """ 

94 if isinstance(other, PathSpec): 

95 self.patterns += other.patterns 

96 return self 

97 else: 

98 return NotImplemented 

99 

100 def check_file( 

101 self, 

102 file: TStrPath, 

103 separators: Optional[Collection[str]] = None, 

104 ) -> CheckResult[TStrPath]: 

105 """ 

106 Check the files against this path-spec. 

107 

108 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be 

109 matched against :attr:`self.patterns <PathSpec.patterns>`. 

110 

111 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or 

112 :data:`None`) optionally contains the path separators to normalize. See 

113 :func:`~pathspec.util.normalize_file` for more information. 

114 

115 Returns the file check result (:class:`~pathspec.util.CheckResult`). 

116 """ 

117 norm_file = normalize_file(file, separators) 

118 include, index = self._match_file(enumerate(self.patterns), norm_file) 

119 return CheckResult(file, include, index) 

120 

121 def check_files( 

122 self, 

123 files: Iterable[TStrPath], 

124 separators: Optional[Collection[str]] = None, 

125 ) -> Iterator[CheckResult[TStrPath]]: 

126 """ 

127 Check the files against this path-spec. 

128 

129 *files* (:class:`~collections.abc.Iterable` of :class:`str` or 

130 :class:`os.PathLike`) contains the file paths to be checked against 

131 :attr:`self.patterns <PathSpec.patterns>`. 

132 

133 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or 

134 :data:`None`) optionally contains the path separators to normalize. See 

135 :func:`~pathspec.util.normalize_file` for more information. 

136 

137 Returns an :class:`~collections.abc.Iterator` yielding each file check 

138 result (:class:`~pathspec.util.CheckResult`). 

139 """ 

140 if not _is_iterable(files): 

141 raise TypeError(f"files:{files!r} is not an iterable.") 

142 

143 use_patterns = _filter_check_patterns(self.patterns) 

144 for orig_file in files: 

145 norm_file = normalize_file(orig_file, separators) 

146 include, index = self._match_file(use_patterns, norm_file) 

147 yield CheckResult(orig_file, include, index) 

148 

149 def check_tree_files( 

150 self, 

151 root: StrPath, 

152 on_error: Optional[Callable[[OSError], None]] = None, 

153 follow_links: Optional[bool] = None, 

154 ) -> Iterator[CheckResult[str]]: 

155 """ 

156 Walks the specified root path for all files and checks them against this 

157 path-spec. 

158 

159 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to 

160 search for files. 

161 

162 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally 

163 is the error handler for file-system exceptions. It will be called with the 

164 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default 

165 is :data:`None` to ignore file-system exceptions. 

166 

167 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk 

168 symbolic links that resolve to directories. Default is :data:`None` for 

169 :data:`True`. 

170 

171 *negate* (:class:`bool` or :data:`None`) is whether to negate the match 

172 results of the patterns. If :data:`True`, a pattern matching a file will 

173 exclude the file rather than include it. Default is :data:`None` for 

174 :data:`False`. 

175 

176 Returns an :class:`~collections.abc.Iterator` yielding each file check 

177 result (:class:`~pathspec.util.CheckResult`). 

178 """ 

179 files = util.iter_tree_files(root, on_error=on_error, follow_links=follow_links) 

180 yield from self.check_files(files) 

181 

182 @classmethod 

183 def from_lines( 

184 cls: Type[Self], 

185 pattern_factory: Union[str, Callable[[AnyStr], Pattern]], 

186 lines: Iterable[AnyStr], 

187 ) -> Self: 

188 """ 

189 Compiles the pattern lines. 

190 

191 *pattern_factory* can be either the name of a registered pattern factory 

192 (:class:`str`), or a :class:`~collections.abc.Callable` used to compile 

193 patterns. It must accept an uncompiled pattern (:class:`str`) and return the 

194 compiled pattern (:class:`.Pattern`). 

195 

196 *lines* (:class:`~collections.abc.Iterable`) yields each uncompiled pattern 

197 (:class:`str`). This simply has to yield each line so that it can be a 

198 :class:`io.TextIOBase` (e.g., from :func:`open` or :class:`io.StringIO`) or 

199 the result from :meth:`str.splitlines`. 

200 

201 Returns the :class:`PathSpec` instance. 

202 """ 

203 if isinstance(pattern_factory, str): 

204 pattern_factory = util.lookup_pattern(pattern_factory) 

205 

206 if not callable(pattern_factory): 

207 raise TypeError(f"pattern_factory:{pattern_factory!r} is not callable.") 

208 

209 if not _is_iterable(lines): 

210 raise TypeError(f"lines:{lines!r} is not an iterable.") 

211 

212 patterns = [pattern_factory(line) for line in lines if line] 

213 return cls(patterns) 

214 

215 def match_entries( 

216 self, 

217 entries: Iterable[TreeEntry], 

218 separators: Optional[Collection[str]] = None, 

219 *, 

220 negate: Optional[bool] = None, 

221 ) -> Iterator[TreeEntry]: 

222 """ 

223 Matches the entries to this path-spec. 

224 

225 *entries* (:class:`~collections.abc.Iterable` of :class:`~pathspec.util.TreeEntry`) 

226 contains the entries to be matched against :attr:`self.patterns <PathSpec.patterns>`. 

227 

228 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or 

229 :data:`None`) optionally contains the path separators to normalize. See 

230 :func:`~pathspec.util.normalize_file` for more information. 

231 

232 *negate* (:class:`bool` or :data:`None`) is whether to negate the match 

233 results of the patterns. If :data:`True`, a pattern matching a file will 

234 exclude the file rather than include it. Default is :data:`None` for 

235 :data:`False`. 

236 

237 Returns the matched entries (:class:`~collections.abc.Iterator` of 

238 :class:`~pathspec.util.TreeEntry`). 

239 """ 

240 if not _is_iterable(entries): 

241 raise TypeError(f"entries:{entries!r} is not an iterable.") 

242 

243 use_patterns = _filter_check_patterns(self.patterns) 

244 for entry in entries: 

245 norm_file = normalize_file(entry.path, separators) 

246 include, _index = self._match_file(use_patterns, norm_file) 

247 

248 if negate: 

249 include = not include 

250 

251 if include: 

252 yield entry 

253 

254 _match_file = staticmethod(util.check_match_file) 

255 """ 

256 Match files using the `check_match_file()` utility function. Subclasses may 

257 override this method as an instance method. It does not have to be a static 

258 method. The signature for this method is subject to change. 

259 """ 

260 

261 def match_file( 

262 self, 

263 file: StrPath, 

264 separators: Optional[Collection[str]] = None, 

265 ) -> bool: 

266 """ 

267 Matches the file to this path-spec. 

268 

269 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be 

270 matched against :attr:`self.patterns <PathSpec.patterns>`. 

271 

272 *separators* (:class:`~collections.abc.Collection` of :class:`str`) 

273 optionally contains the path separators to normalize. See 

274 :func:`~pathspec.util.normalize_file` for more information. 

275 

276 Returns :data:`True` if *file* matched; otherwise, :data:`False`. 

277 """ 

278 norm_file = normalize_file(file, separators) 

279 include, _index = self._match_file(enumerate(self.patterns), norm_file) 

280 return bool(include) 

281 

282 def match_files( 

283 self, 

284 files: Iterable[StrPath], 

285 separators: Optional[Collection[str]] = None, 

286 *, 

287 negate: Optional[bool] = None, 

288 ) -> Iterator[StrPath]: 

289 """ 

290 Matches the files to this path-spec. 

291 

292 *files* (:class:`~collections.abc.Iterable` of :class:`str` or 

293 :class:`os.PathLike`) contains the file paths to be matched against 

294 :attr:`self.patterns <PathSpec.patterns>`. 

295 

296 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or 

297 :data:`None`) optionally contains the path separators to normalize. See 

298 :func:`~pathspec.util.normalize_file` for more information. 

299 

300 *negate* (:class:`bool` or :data:`None`) is whether to negate the match 

301 results of the patterns. If :data:`True`, a pattern matching a file will 

302 exclude the file rather than include it. Default is :data:`None` for 

303 :data:`False`. 

304 

305 Returns the matched files (:class:`~collections.abc.Iterator` of 

306 :class:`str` or :class:`os.PathLike`). 

307 """ 

308 if not _is_iterable(files): 

309 raise TypeError(f"files:{files!r} is not an iterable.") 

310 

311 use_patterns = _filter_check_patterns(self.patterns) 

312 for orig_file in files: 

313 norm_file = normalize_file(orig_file, separators) 

314 include, _index = self._match_file(use_patterns, norm_file) 

315 

316 if negate: 

317 include = not include 

318 

319 if include: 

320 yield orig_file 

321 

322 def match_tree_entries( 

323 self, 

324 root: StrPath, 

325 on_error: Optional[Callable[[OSError], None]] = None, 

326 follow_links: Optional[bool] = None, 

327 *, 

328 negate: Optional[bool] = None, 

329 ) -> Iterator[TreeEntry]: 

330 """ 

331 Walks the specified root path for all files and matches them to this 

332 path-spec. 

333 

334 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to 

335 search. 

336 

337 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally 

338 is the error handler for file-system exceptions. It will be called with the 

339 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default 

340 is :data:`None` to ignore file-system exceptions. 

341 

342 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk 

343 symbolic links that resolve to directories. Default is :data:`None` for 

344 :data:`True`. 

345 

346 *negate* (:class:`bool` or :data:`None`) is whether to negate the match 

347 results of the patterns. If :data:`True`, a pattern matching a file will 

348 exclude the file rather than include it. Default is :data:`None` for 

349 :data:`False`. 

350 

351 Returns the matched files (:class:`~collections.abc.Iterator` of 

352 :class:`.TreeEntry`). 

353 """ 

354 entries = util.iter_tree_entries(root, on_error=on_error, follow_links=follow_links) 

355 yield from self.match_entries(entries, negate=negate) 

356 

357 def match_tree_files( 

358 self, 

359 root: StrPath, 

360 on_error: Optional[Callable[[OSError], None]] = None, 

361 follow_links: Optional[bool] = None, 

362 *, 

363 negate: Optional[bool] = None, 

364 ) -> Iterator[str]: 

365 """ 

366 Walks the specified root path for all files and matches them to this 

367 path-spec. 

368 

369 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to 

370 search for files. 

371 

372 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally 

373 is the error handler for file-system exceptions. It will be called with the 

374 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default 

375 is :data:`None` to ignore file-system exceptions. 

376 

377 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk 

378 symbolic links that resolve to directories. Default is :data:`None` for 

379 :data:`True`. 

380 

381 *negate* (:class:`bool` or :data:`None`) is whether to negate the match 

382 results of the patterns. If :data:`True`, a pattern matching a file will 

383 exclude the file rather than include it. Default is :data:`None` for 

384 :data:`False`. 

385 

386 Returns the matched files (:class:`~collections.abc.Iterable` of 

387 :class:`str`). 

388 """ 

389 files = util.iter_tree_files(root, on_error=on_error, follow_links=follow_links) 

390 yield from self.match_files(files, negate=negate) 

391 

392 # Alias `match_tree_files()` as `match_tree()` for backward compatibility 

393 # before v0.3.2. 

394 match_tree = match_tree_files