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

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

102 statements  

1""" 

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

3""" 

4 

5from collections.abc import ( 

6 Callable, 

7 Collection, 

8 Iterable, 

9 Iterator, 

10 Sequence) 

11from itertools import ( 

12 zip_longest) 

13from typing import ( 

14 AnyStr, 

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

16 TypeVar, 

17 Union, # Replaced by `X | Y` in 3.10. 

18 cast) 

19 

20from . import util 

21from ._backends.base import ( 

22 Backend, 

23 BackendNamesHint) 

24from ._backends.agg import ( 

25 make_pathspec_backend) 

26from .pattern import ( 

27 Pattern) 

28from .util import ( 

29 CheckResult, 

30 StrPath, 

31 TStrPath, 

32 TreeEntry, 

33 _is_iterable, 

34 normalize_file) 

35 

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

37""" 

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

39recommendation. 

40""" 

41 

42 

43class PathSpec(object): 

44 """ 

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

46 :class:`.Pattern` instances. 

47 """ 

48 

49 def __init__( 

50 self, 

51 patterns: Union[Sequence[Pattern], Iterable[Pattern]], 

52 *, 

53 backend: Union[BackendNamesHint, str, None] = None, 

54 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], Backend]] = None, 

55 ) -> None: 

56 """ 

57 Initializes the :class:`PathSpec` instance. 

58 

59 *patterns* (:class:`~collections.abc.Sequence` or :class:`~collections.abc.Iterable`) 

60 contains each compiled pattern (:class:`.Pattern`). If not a sequence, it 

61 will be converted to a :class:`list`. 

62 

63 *backend* (:class:`str` or :data:`None`) is the pattern (or regex) matching 

64 backend to use. Default is :data:`None` for "best" to use the best available 

65 backend. Priority of backends is: "hyperscan", "simple". The "simple" 

66 backend is always available. 

67 """ 

68 if not isinstance(patterns, Sequence): 

69 patterns = list(patterns) 

70 

71 if backend is None: 

72 backend = 'best' 

73 

74 backend = cast(BackendNamesHint, backend) 

75 if _test_backend_factory is not None: 

76 use_backend = _test_backend_factory(patterns) 

77 else: 

78 use_backend = self._make_backend(backend, patterns) 

79 

80 self._backend: Backend = use_backend 

81 """ 

82 *_backend* (:class:`.Backend`) is the pattern (or regex) matching backend. 

83 """ 

84 

85 self._backend_name: BackendNamesHint = backend 

86 """ 

87 *_backend_name* (:class:`str`) is the name of backend to use. 

88 """ 

89 

90 self.patterns: Sequence[Pattern] = patterns 

91 """ 

92 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`) 

93 contains the compiled patterns. 

94 """ 

95 

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

97 """ 

98 Combines the :attr:`Pathspec.patterns` patterns from two :class:`PathSpec` 

99 instances. 

100 """ 

101 if isinstance(other, PathSpec): 

102 return self.__class__(self.patterns + other.patterns, backend=self._backend_name) 

103 else: 

104 return NotImplemented 

105 

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

107 """ 

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

109 comparing their :attr:`~PathSpec.patterns` attributes. 

110 """ 

111 if isinstance(other, PathSpec): 

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

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

114 else: 

115 return NotImplemented 

116 

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

118 """ 

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

120 instance to this instance. 

121 """ 

122 if isinstance(other, PathSpec): 

123 self.patterns += other.patterns 

124 self._backend = self._make_backend(self._backend_name, self.patterns) 

125 return self 

126 else: 

127 return NotImplemented 

128 

129 def __len__(self) -> int: 

130 """ 

131 Returns the number of compiled patterns this path-spec contains (:class:`int`). 

132 """ 

133 return len(self.patterns) 

134 

135 def check_file( 

136 self, 

137 file: TStrPath, 

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

139 ) -> CheckResult[TStrPath]: 

140 """ 

141 Check the files against this path-spec. 

142 

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

144 against :attr:`self.patterns <PathSpec.patterns>`. 

145 

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

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

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

149 

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

151 """ 

152 norm_file = normalize_file(file, separators) 

153 include, index = self._backend.match_file(norm_file) 

154 return CheckResult(file, include, index) 

155 

156 def check_files( 

157 self, 

158 files: Iterable[TStrPath], 

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

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

161 """ 

162 Check the files against this path-spec. 

163 

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

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

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

167 

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

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

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

171 

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

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

174 """ 

175 if not _is_iterable(files): 

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

177 

178 for orig_file in files: 

179 norm_file = normalize_file(orig_file, separators) 

180 include, index = self._backend.match_file(norm_file) 

181 yield CheckResult(orig_file, include, index) 

182 

183 def check_tree_files( 

184 self, 

185 root: StrPath, 

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

187 follow_links: Optional[bool] = None, 

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

189 """ 

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

191 path-spec. 

192 

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

194 search for files. 

195 

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

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

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

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

200 

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

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

203 :data:`True`. 

204 

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

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

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

208 :data:`False`. 

209 

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

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

212 """ 

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

214 yield from self.check_files(files) 

215 

216 @classmethod 

217 def from_lines( 

218 cls: type[Self], 

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

220 lines: Iterable[AnyStr], 

221 *, 

222 backend: Union[BackendNamesHint, str, None] = None, 

223 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], Backend]] = None, 

224 ) -> Self: 

225 """ 

226 Compiles the pattern lines. 

227 

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

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

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

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

232 

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

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

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

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

237 

238 *backend* (:class:`str` or :data:`None`) is the pattern (or regex) matching 

239 backend to use. Default is :data:`None` for "best" to use the best available 

240 backend. Priority of backends is: "hyperscan", "simple". The "simple" 

241 backend is always available. 

242 

243 Returns the :class:`PathSpec` instance. 

244 """ 

245 if isinstance(pattern_factory, str): 

246 pattern_factory = util.lookup_pattern(pattern_factory) 

247 

248 if not callable(pattern_factory): 

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

250 

251 if not _is_iterable(lines): 

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

253 

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

255 return cls(patterns, backend=backend, _test_backend_factory=_test_backend_factory) 

256 

257 @staticmethod 

258 def _make_backend( 

259 name: BackendNamesHint, 

260 patterns: Sequence[Pattern], 

261 ) -> Backend: 

262 """ 

263 Create the backend for the patterns. 

264 

265 *name* (:class:`str`) is the name of the backend. 

266 

267 *patterns* (:class:`.Sequence` of :class:`.Pattern`) contains the compiled 

268 patterns. 

269 

270 Returns the matcher (:class:`.Backend`). 

271 """ 

272 return make_pathspec_backend(name, patterns) 

273 

274 def match_entries( 

275 self, 

276 entries: Iterable[TreeEntry], 

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

278 *, 

279 negate: Optional[bool] = None, 

280 ) -> Iterator[TreeEntry]: 

281 """ 

282 Matches the entries to this path-spec. 

283 

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

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

286 

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

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

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

290 

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

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

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

294 :data:`False`. 

295 

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

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

298 """ 

299 if not _is_iterable(entries): 

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

301 

302 for entry in entries: 

303 norm_file = normalize_file(entry.path, separators) 

304 include, _index = self._backend.match_file(norm_file) 

305 

306 if negate: 

307 include = not include 

308 

309 if include: 

310 yield entry 

311 

312 def match_file( 

313 self, 

314 file: StrPath, 

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

316 ) -> bool: 

317 """ 

318 Matches the file to this path-spec. 

319 

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

321 against :attr:`self.patterns <PathSpec.patterns>`. 

322 

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

324 optionally contains the path separators to normalize. See 

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

326 

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

328 """ 

329 norm_file = normalize_file(file, separators) 

330 include, _index = self._backend.match_file(norm_file) 

331 return bool(include) 

332 

333 def match_files( 

334 self, 

335 files: Iterable[StrPath], 

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

337 *, 

338 negate: Optional[bool] = None, 

339 ) -> Iterator[StrPath]: 

340 """ 

341 Matches the files to this path-spec. 

342 

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

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

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

346 

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

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

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

350 

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

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

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

354 :data:`False`. 

355 

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

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

358 """ 

359 if not _is_iterable(files): 

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

361 

362 for orig_file in files: 

363 norm_file = normalize_file(orig_file, separators) 

364 include, _index = self._backend.match_file(norm_file) 

365 

366 if negate: 

367 include = not include 

368 

369 if include: 

370 yield orig_file 

371 

372 def match_tree_entries( 

373 self, 

374 root: StrPath, 

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

376 follow_links: Optional[bool] = None, 

377 *, 

378 negate: Optional[bool] = None, 

379 ) -> Iterator[TreeEntry]: 

380 """ 

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

382 path-spec. 

383 

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

385 search. 

386 

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

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

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

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

391 

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

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

394 :data:`True`. 

395 

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

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

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

399 :data:`False`. 

400 

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

402 :class:`.TreeEntry`). 

403 """ 

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

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

406 

407 def match_tree_files( 

408 self, 

409 root: StrPath, 

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

411 follow_links: Optional[bool] = None, 

412 *, 

413 negate: Optional[bool] = None, 

414 ) -> Iterator[str]: 

415 """ 

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

417 path-spec. 

418 

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

420 search for files. 

421 

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

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

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

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

426 

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

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

429 :data:`True`. 

430 

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

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

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

434 :data:`False`. 

435 

436 Returns the matched files (:class:`~collections.abc.Iterable` of :class:`str`). 

437 """ 

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

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

440 

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

442 # before v0.3.2. 

443 match_tree = match_tree_files