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

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

128 statements  

1""" 

2This module provides :class:`.PathSpec` which is an object-oriented interface 

3for pattern matching of files. 

4""" 

5from __future__ import annotations 

6 

7from collections.abc import ( 

8 Collection, 

9 Iterable, 

10 Iterator, 

11 Sequence) 

12from itertools import ( 

13 zip_longest) 

14from typing import ( 

15 Callable, # Replaced by `collections.abc.Callable` in 3.9.2. 

16 Generic, 

17 Literal, 

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

19 TypeVar, 

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

21 cast, 

22 overload) 

23 

24Self = TypeVar("Self", bound='PathSpec') 

25""" 

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

27recommendation. 

28""" 

29 

30from pathspec import util 

31from pathspec.backend import ( 

32 BackendNamesHint, 

33 _Backend, 

34 _TestBackendFactoryHint) 

35from pathspec._backends.agg import ( 

36 make_pathspec_backend) 

37from pathspec.pattern import ( 

38 Pattern) 

39from pathspec.patterns.gitignore.basic import ( 

40 GitIgnoreBasicPattern) 

41from pathspec._typing import ( 

42 AnyStr, # Removed in 3.18. 

43 deprecated) # Added in 3.13. 

44from pathspec.util import ( 

45 CheckResult, 

46 StrPath, 

47 TPattern, 

48 TPattern_co, 

49 TStrPath, 

50 TreeEntry, 

51 _is_iterable, 

52 normalize_file) 

53 

54 

55class PathSpec(Generic[TPattern_co]): 

56 """ 

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

58 :class:`.Pattern` instances. 

59 """ 

60 

61 def __init__( 

62 self, 

63 patterns: Union[Sequence[TPattern_co], Iterable[TPattern_co]], 

64 *, 

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

66 _test_backend_factory: _TestBackendFactoryHint = None, 

67 ) -> None: 

68 """ 

69 Initializes the :class:`.PathSpec` instance. 

70 

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

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

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

74 

75 *backend* (:class:`str` or :data:`None`) is the pattern (regular expression) 

76 matching backend to use. Default is :data:`None` for "best" to use the best 

77 available backend. Priority of backends is: "re2", "hyperscan", "simple". 

78 The "simple" backend is always available. 

79 """ 

80 if isinstance(patterns, Sequence): 

81 use_patterns = patterns 

82 else: 

83 use_patterns = list(patterns) 

84 

85 if backend is None: 

86 backend = 'best' 

87 

88 backend_name = cast(BackendNamesHint, backend) 

89 if _test_backend_factory is not None: 

90 use_backend = _test_backend_factory(use_patterns) 

91 else: 

92 use_backend = self._make_backend(backend_name, use_patterns) 

93 

94 self._backend: _Backend = use_backend 

95 """ 

96 *_backend* (:class:`._Backend`) is the pattern (regular expression) matching 

97 backend. 

98 """ 

99 

100 self._backend_name: BackendNamesHint = backend_name 

101 """ 

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

103 """ 

104 

105 self.patterns: Sequence[TPattern_co] = use_patterns 

106 """ 

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

108 contains the compiled patterns. 

109 """ 

110 

111 def __repr__(self) -> str: 

112 """ 

113 Returns a debug representation of this path-spec. 

114 """ 

115 return f"{self.__class__.__name__}(patterns={self.patterns!r}, backend={self._backend_name!r})" 

116 

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

118 """ 

119 Combines the :attr:`self.patterns <.PathSpec.patterns>` patterns from two 

120 :class:`PathSpec` instances. 

121 """ 

122 if isinstance(other, PathSpec): 

123 return self.__class__([*self.patterns, *other.patterns], backend=self._backend_name) 

124 else: 

125 return NotImplemented 

126 

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

128 """ 

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

130 comparing their :attr:`self.patterns <.PathSpec.patterns>` attributes. 

131 """ 

132 if isinstance(other, PathSpec): 

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

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

135 else: 

136 return NotImplemented 

137 

138 def __iadd__(self: Self, other: PathSpec) -> Self: # type: ignore[misc] 

139 """ 

140 Adds the :attr:`self.patterns <.PathSpec.patterns>` from *other* 

141 (:class:`PathSpec`) to this instance. 

142 """ 

143 if isinstance(other, PathSpec): 

144 self.patterns = [*self.patterns, *other.patterns] 

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

146 return self 

147 else: 

148 return NotImplemented 

149 

150 def __len__(self) -> int: 

151 """ 

152 Returns the number of :attr:`self.patterns <.PathSpec.patterns>` this 

153 path-spec contains (:class:`int`). 

154 """ 

155 return len(self.patterns) 

156 

157 def check_file( 

158 self, 

159 file: TStrPath, 

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

161 ) -> CheckResult[TStrPath]: 

162 """ 

163 Check the files against this path-spec. 

164 

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

166 against :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:`.normalize_file` for more information. 

171 

172 Returns the file check result (:class:`.CheckResult`). 

173 """ 

174 norm_file = normalize_file(file, separators) 

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

176 return CheckResult(file, include, index) 

177 

178 def check_files( 

179 self, 

180 files: Iterable[TStrPath], 

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

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

183 """ 

184 Check the files against this path-spec. 

185 

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

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

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

189 

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

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

192 :func:`.normalize_file` for more information. 

193 

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

195 result (:class:`.CheckResult`). 

196 """ 

197 if not _is_iterable(files): 

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

199 

200 for orig_file in files: 

201 norm_file = normalize_file(orig_file, separators) 

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

203 yield CheckResult(orig_file, include, index) 

204 

205 def check_tree_files( 

206 self, 

207 root: StrPath, 

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

209 follow_links: Optional[bool] = None, 

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

211 """ 

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

213 path-spec. 

214 

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

216 search for files. 

217 

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

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

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

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

222 

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

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

225 :data:`True`. 

226 

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

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

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

230 :data:`False`. 

231 

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

233 result (:class:`.CheckResult`). 

234 """ 

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

236 yield from self.check_files(files) 

237 

238 @overload 

239 @classmethod 

240 def from_lines( 

241 cls: type[PathSpec], 

242 pattern_factory: Literal['gitignore'], 

243 lines: Iterable[AnyStr], 

244 *, 

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

246 _test_backend_factory: _TestBackendFactoryHint = None, 

247 ) -> PathSpec[GitIgnoreBasicPattern]: 

248 ... 

249 

250 @overload 

251 @classmethod 

252 def from_lines( 

253 cls: type[PathSpec], 

254 pattern_factory: str, 

255 lines: Iterable[AnyStr], 

256 *, 

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

258 _test_backend_factory: _TestBackendFactoryHint = None, 

259 ) -> PathSpec[Pattern]: 

260 ... 

261 

262 @overload 

263 @classmethod 

264 def from_lines( 

265 cls: type[PathSpec], 

266 pattern_factory: type[TPattern], 

267 lines: Iterable[AnyStr], 

268 *, 

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

270 _test_backend_factory: _TestBackendFactoryHint = None, 

271 ) -> PathSpec[TPattern]: 

272 ... 

273 

274 @overload 

275 @classmethod 

276 def from_lines( 

277 cls: type[PathSpec], 

278 pattern_factory: Callable[[AnyStr], TPattern], 

279 lines: Iterable[AnyStr], 

280 *, 

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

282 _test_backend_factory: _TestBackendFactoryHint = None, 

283 ) -> PathSpec[TPattern]: 

284 ... 

285 

286 @classmethod 

287 def from_lines( 

288 cls: type[Self], 

289 pattern_factory: Union[str, type[Pattern], Callable[[AnyStr], Pattern]], 

290 lines: Iterable[AnyStr], 

291 *, 

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

293 _test_backend_factory: _TestBackendFactoryHint = None, 

294 ) -> Self: 

295 """ 

296 Compiles the pattern lines. 

297 

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

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

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

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

302 

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

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

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

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

307 

308 *backend* (:class:`str` or :data:`None`) is the pattern (or regular 

309 expression) matching backend to use. Default is :data:`None` for "best" to 

310 use the best available backend. Priority of backends is: "re2", "hyperscan", 

311 "simple". The "simple" backend is always available. 

312 

313 Returns the :class:`PathSpec` instance. 

314 """ 

315 use_factory: Callable[[AnyStr], Pattern] 

316 if isinstance(pattern_factory, str): 

317 use_factory = util.lookup_pattern(pattern_factory) # type: ignore[assignment] 

318 elif callable(pattern_factory): 

319 use_factory = pattern_factory # type: ignore[assignment] 

320 else: 

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

322 

323 if not _is_iterable(lines): 

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

325 

326 patterns = [use_factory(__line) for __line in lines if __line] # type: ignore[arg-type] 

327 self = cls(patterns, backend=backend, _test_backend_factory=_test_backend_factory) 

328 return self 

329 

330 @staticmethod 

331 def _make_backend( 

332 name: BackendNamesHint, 

333 patterns: Sequence[Pattern], 

334 ) -> _Backend: 

335 """ 

336 .. warning:: This method is not part of the public API. It is subject to 

337 change. 

338 

339 Create the backend for the patterns. 

340 

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

342 

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

344 contains the compiled patterns. 

345 

346 Returns the matcher (:class:`._Backend`). 

347 """ 

348 return make_pathspec_backend(name, patterns) 

349 

350 def match_entries( 

351 self, 

352 entries: Iterable[TreeEntry], 

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

354 *, 

355 negate: Optional[bool] = None, 

356 ) -> Iterator[TreeEntry]: 

357 """ 

358 Matches the entries to this path-spec. 

359 

360 *entries* (:class:`~collections.abc.Iterable` of :class:`.TreeEntry`) 

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

362 

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

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

365 :func:`.normalize_file` for more information. 

366 

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

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

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

370 :data:`False`. 

371 

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

373 :class:`.TreeEntry`). 

374 """ 

375 if not _is_iterable(entries): 

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

377 

378 for entry in entries: 

379 norm_file = normalize_file(entry.path, separators) 

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

381 

382 if negate: 

383 include = not include 

384 

385 if include: 

386 yield entry 

387 

388 def match_file( 

389 self, 

390 file: StrPath, 

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

392 ) -> bool: 

393 """ 

394 Matches the file to this path-spec. 

395 

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

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

398 

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

400 optionally contains the path separators to normalize. See 

401 :func:`.normalize_file` for more information. 

402 

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

404 """ 

405 norm_file = normalize_file(file, separators) 

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

407 return bool(include) 

408 

409 def match_files( 

410 self, 

411 files: Iterable[TStrPath], 

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

413 *, 

414 negate: Optional[bool] = None, 

415 ) -> Iterator[TStrPath]: 

416 """ 

417 Matches the files to this path-spec. 

418 

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

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

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

422 

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

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

425 :func:`.normalize_file` for more information. 

426 

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

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

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

430 :data:`False`. 

431 

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

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

434 """ 

435 if not _is_iterable(files): 

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

437 

438 for orig_file in files: 

439 norm_file = normalize_file(orig_file, separators) 

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

441 

442 if negate: 

443 include = not include 

444 

445 if include: 

446 yield orig_file 

447 

448 def match_tree_entries( 

449 self, 

450 root: StrPath, 

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

452 follow_links: Optional[bool] = None, 

453 *, 

454 negate: Optional[bool] = None, 

455 ) -> Iterator[TreeEntry]: 

456 """ 

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

458 path-spec. 

459 

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

461 search. 

462 

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

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

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

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

467 

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

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

470 :data:`True`. 

471 

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

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

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

475 :data:`False`. 

476 

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

478 :class:`.TreeEntry`). 

479 """ 

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

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

482 

483 # NOTICE: The deprecation warning was only added in 1.0.0 (from 2026-01-05). 

484 @deprecated(( 

485 "PathSpec.match_tree() is deprecated. Use .match_tree_files() instead." 

486 )) 

487 def match_tree(self, *args, **kw) -> Iterator[str]: 

488 """ 

489 .. version-deprecated:: 0.3.2 

490 This is an alias for the :meth:`self.match_tree_files <.PathSpec.match_tree_files>` 

491 method. 

492 """ 

493 return self.match_tree_files(*args, **kw) 

494 

495 def match_tree_files( 

496 self, 

497 root: StrPath, 

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

499 follow_links: Optional[bool] = None, 

500 *, 

501 negate: Optional[bool] = None, 

502 ) -> Iterator[str]: 

503 """ 

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

505 path-spec. 

506 

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

508 search for files. 

509 

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

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

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

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

514 

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

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

517 :data:`True`. 

518 

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

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

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

522 :data:`False`. 

523 

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

525 """ 

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

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