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

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

132 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. Automatic selection uses "simple" 

79 when any pattern is not a :class:`.RegexPattern`. Native backends only 

80 support regular expressions. 

81 """ 

82 if isinstance(patterns, Sequence): 

83 use_patterns = patterns 

84 else: 

85 use_patterns = list(patterns) 

86 

87 if backend is None: 

88 backend = 'best' 

89 

90 backend_name = cast(BackendNamesHint, backend) 

91 if _test_backend_factory is not None: 

92 use_backend = _test_backend_factory(use_patterns) 

93 else: 

94 use_backend = self._make_backend(backend_name, use_patterns) 

95 

96 self._backend: _Backend = use_backend 

97 """ 

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

99 backend. 

100 """ 

101 

102 self._backend_name: BackendNamesHint = backend_name 

103 """ 

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

105 """ 

106 

107 self.patterns: Sequence[TPattern_co] = use_patterns 

108 """ 

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

110 contains the compiled patterns. 

111 """ 

112 

113 def __repr__(self) -> str: 

114 """ 

115 Returns a debug representation of this path-spec. 

116 """ 

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

118 

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

120 """ 

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

122 :class:`PathSpec` instances. 

123 """ 

124 if isinstance(other, PathSpec): 

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

126 else: 

127 return NotImplemented 

128 

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

130 """ 

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

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

133 """ 

134 if isinstance(other, PathSpec): 

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

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

137 else: 

138 return NotImplemented 

139 

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

141 """ 

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

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

144 """ 

145 if isinstance(other, PathSpec): 

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

147 backend = self._make_backend(self._backend_name, patterns) 

148 self.patterns = patterns 

149 self._backend = backend 

150 return self 

151 else: 

152 return NotImplemented 

153 

154 def __len__(self) -> int: 

155 """ 

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

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

158 """ 

159 return len(self.patterns) 

160 

161 def check_file( 

162 self, 

163 file: TStrPath, 

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

165 ) -> CheckResult[TStrPath]: 

166 """ 

167 Check the files against this path-spec. 

168 

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

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

171 

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

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

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

175 

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

177 """ 

178 norm_file = normalize_file(file, separators) 

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

180 return CheckResult(file, include, index) 

181 

182 def check_files( 

183 self, 

184 files: Iterable[TStrPath], 

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

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

187 """ 

188 Check the files against this path-spec. 

189 

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

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

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

193 

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

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

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

197 

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

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

200 """ 

201 if not _is_iterable(files): 

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

203 

204 for orig_file in files: 

205 norm_file = normalize_file(orig_file, separators) 

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

207 yield CheckResult(orig_file, include, index) 

208 

209 def check_tree_files( 

210 self, 

211 root: StrPath, 

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

213 follow_links: Optional[bool] = None, 

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

215 """ 

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

217 path-spec. 

218 

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

220 search for files. 

221 

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

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

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

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

226 

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

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

229 :data:`True`. 

230 

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

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

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

234 :data:`False`. 

235 

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

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

238 """ 

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

240 yield from self.check_files(files) 

241 

242 @overload 

243 @classmethod 

244 def from_lines( 

245 cls: type[PathSpec], 

246 pattern_factory: Literal['gitignore'], 

247 lines: Iterable[AnyStr], 

248 *, 

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

250 _test_backend_factory: _TestBackendFactoryHint = None, 

251 ) -> PathSpec[GitIgnoreBasicPattern]: 

252 ... 

253 

254 @overload 

255 @classmethod 

256 def from_lines( 

257 cls: type[PathSpec], 

258 pattern_factory: str, 

259 lines: Iterable[AnyStr], 

260 *, 

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

262 _test_backend_factory: _TestBackendFactoryHint = None, 

263 ) -> PathSpec[Pattern]: 

264 ... 

265 

266 @overload 

267 @classmethod 

268 def from_lines( 

269 cls: type[PathSpec], 

270 pattern_factory: type[TPattern], 

271 lines: Iterable[AnyStr], 

272 *, 

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

274 _test_backend_factory: _TestBackendFactoryHint = None, 

275 ) -> PathSpec[TPattern]: 

276 ... 

277 

278 @overload 

279 @classmethod 

280 def from_lines( 

281 cls: type[PathSpec], 

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

283 lines: Iterable[AnyStr], 

284 *, 

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

286 _test_backend_factory: _TestBackendFactoryHint = None, 

287 ) -> PathSpec[TPattern]: 

288 ... 

289 

290 @classmethod 

291 def from_lines( 

292 cls: type[Self], 

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

294 lines: Iterable[AnyStr], 

295 *, 

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

297 _test_backend_factory: _TestBackendFactoryHint = None, 

298 ) -> Self: 

299 """ 

300 Compiles the pattern lines. 

301 

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

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

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

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

306 

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

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

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

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

311 

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

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

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

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

316 

317 Returns the :class:`PathSpec` instance. 

318 """ 

319 use_factory: Callable[[AnyStr], Pattern] 

320 if isinstance(pattern_factory, str): 

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

322 elif callable(pattern_factory): 

323 use_factory = pattern_factory # type: ignore[assignment] 

324 else: 

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

326 

327 if not _is_iterable(lines): 

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

329 

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

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

332 return self 

333 

334 @staticmethod 

335 def _make_backend( 

336 name: BackendNamesHint, 

337 patterns: Sequence[Pattern], 

338 ) -> _Backend: 

339 """ 

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

341 change. 

342 

343 Create the backend for the patterns. 

344 

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

346 

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

348 contains the compiled patterns. 

349 

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

351 """ 

352 return make_pathspec_backend(name, patterns) 

353 

354 def match_entries( 

355 self, 

356 entries: Iterable[TreeEntry], 

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

358 *, 

359 negate: Optional[bool] = None, 

360 ) -> Iterator[TreeEntry]: 

361 """ 

362 Matches the entries to this path-spec. 

363 

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

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

366 Directory entries are matched with a trailing path separator, without 

367 changing their :attr:`~.TreeEntry.path` attributes. 

368 

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

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

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

372 

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

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

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

376 :data:`False`. 

377 

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

379 :class:`.TreeEntry`). 

380 """ 

381 if not _is_iterable(entries): 

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

383 

384 for entry in entries: 

385 norm_file = normalize_file(entry.path, separators) 

386 if entry.is_dir() and not norm_file.endswith('/'): 

387 norm_file += '/' 

388 

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

390 

391 if negate: 

392 include = not include 

393 

394 if include: 

395 yield entry 

396 

397 def match_file( 

398 self, 

399 file: StrPath, 

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

401 ) -> bool: 

402 """ 

403 Matches the file to this path-spec. 

404 

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

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

407 

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

409 optionally contains the path separators to normalize. See 

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

411 

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

413 """ 

414 norm_file = normalize_file(file, separators) 

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

416 return bool(include) 

417 

418 def match_files( 

419 self, 

420 files: Iterable[TStrPath], 

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

422 *, 

423 negate: Optional[bool] = None, 

424 ) -> Iterator[TStrPath]: 

425 """ 

426 Matches the files to this path-spec. 

427 

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

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

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

431 

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

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

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

435 

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

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

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

439 :data:`False`. 

440 

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

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

443 """ 

444 if not _is_iterable(files): 

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

446 

447 for orig_file in files: 

448 norm_file = normalize_file(orig_file, separators) 

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

450 

451 if negate: 

452 include = not include 

453 

454 if include: 

455 yield orig_file 

456 

457 def match_tree_entries( 

458 self, 

459 root: StrPath, 

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

461 follow_links: Optional[bool] = None, 

462 *, 

463 negate: Optional[bool] = None, 

464 ) -> Iterator[TreeEntry]: 

465 """ 

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

467 path-spec. 

468 

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

470 search. 

471 

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

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

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

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

476 

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

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

479 :data:`True`. 

480 

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

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

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

484 :data:`False`. 

485 

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

487 :class:`.TreeEntry`). 

488 """ 

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

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

491 

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

493 @deprecated(( 

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

495 )) 

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

497 """ 

498 .. version-deprecated:: 0.3.2 

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

500 method. 

501 """ 

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

503 

504 def match_tree_files( 

505 self, 

506 root: StrPath, 

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

508 follow_links: Optional[bool] = None, 

509 *, 

510 negate: Optional[bool] = None, 

511 ) -> Iterator[str]: 

512 """ 

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

514 path-spec. 

515 

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

517 search for files. 

518 

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

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

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

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

523 

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

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

526 :data:`True`. 

527 

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

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

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

531 :data:`False`. 

532 

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

534 """ 

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

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