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

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

108 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 Optional, # Replaced by `X | None` in 3.10. 

17 TypeVar, 

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

19 cast) 

20 

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

22""" 

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

24recommendation. 

25""" 

26 

27from pathspec import util 

28from pathspec.backend import ( 

29 _Backend, 

30 BackendNamesHint) 

31from pathspec._backends.agg import ( 

32 make_pathspec_backend) 

33from pathspec.pattern import ( 

34 Pattern) 

35from pathspec._typing import ( 

36 AnyStr, # Removed in 3.18. 

37 deprecated) # Added in 3.13. 

38from pathspec.util import ( 

39 CheckResult, 

40 StrPath, 

41 TStrPath, 

42 TreeEntry, 

43 _is_iterable, 

44 normalize_file) 

45 

46 

47class PathSpec(object): 

48 """ 

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

50 :class:`.Pattern` instances. 

51 """ 

52 

53 def __init__( 

54 self, 

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

56 *, 

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

58 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], _Backend]] = None, 

59 ) -> None: 

60 """ 

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

62 

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

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

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

66 

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

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

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

70 The "simple" backend is always available. 

71 """ 

72 if not isinstance(patterns, Sequence): 

73 patterns = list(patterns) 

74 

75 if backend is None: 

76 backend = 'best' 

77 

78 backend = cast(BackendNamesHint, backend) 

79 if _test_backend_factory is not None: 

80 use_backend = _test_backend_factory(patterns) 

81 else: 

82 use_backend = self._make_backend(backend, patterns) 

83 

84 self._backend: _Backend = use_backend 

85 """ 

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

87 backend. 

88 """ 

89 

90 self._backend_name: BackendNamesHint = backend 

91 """ 

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

93 """ 

94 

95 self.patterns: Sequence[Pattern] = patterns 

96 """ 

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

98 contains the compiled patterns. 

99 """ 

100 

101 def __repr__(self) -> str: 

102 """ 

103 Returns a debug representation of this path-spec. 

104 """ 

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

106 

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

108 """ 

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

110 :class:`PathSpec` instances. 

111 """ 

112 if isinstance(other, PathSpec): 

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

114 else: 

115 return NotImplemented 

116 

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

118 """ 

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

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

121 """ 

122 if isinstance(other, PathSpec): 

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

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

125 else: 

126 return NotImplemented 

127 

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

129 """ 

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

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

132 """ 

133 if isinstance(other, PathSpec): 

134 self.patterns += other.patterns 

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

136 return self 

137 else: 

138 return NotImplemented 

139 

140 def __len__(self) -> int: 

141 """ 

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

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

144 """ 

145 return len(self.patterns) 

146 

147 def check_file( 

148 self, 

149 file: TStrPath, 

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

151 ) -> CheckResult[TStrPath]: 

152 """ 

153 Check the files against this path-spec. 

154 

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

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

157 

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

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

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

161 

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

163 """ 

164 norm_file = normalize_file(file, separators) 

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

166 return CheckResult(file, include, index) 

167 

168 def check_files( 

169 self, 

170 files: Iterable[TStrPath], 

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

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

173 """ 

174 Check the files against this path-spec. 

175 

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

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

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

179 

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

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

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

183 

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

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

186 """ 

187 if not _is_iterable(files): 

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

189 

190 for orig_file in files: 

191 norm_file = normalize_file(orig_file, separators) 

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

193 yield CheckResult(orig_file, include, index) 

194 

195 def check_tree_files( 

196 self, 

197 root: StrPath, 

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

199 follow_links: Optional[bool] = None, 

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

201 """ 

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

203 path-spec. 

204 

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

206 search for files. 

207 

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

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

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

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

212 

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

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

215 :data:`True`. 

216 

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

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

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

220 :data:`False`. 

221 

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

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

224 """ 

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

226 yield from self.check_files(files) 

227 

228 @classmethod 

229 def from_lines( 

230 cls: type[Self], 

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

232 lines: Iterable[AnyStr], 

233 *, 

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

235 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], _Backend]] = None, 

236 ) -> Self: 

237 """ 

238 Compiles the pattern lines. 

239 

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

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

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

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

244 

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

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

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

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

249 

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

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

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

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

254 

255 Returns the :class:`PathSpec` instance. 

256 """ 

257 if isinstance(pattern_factory, str): 

258 pattern_factory = util.lookup_pattern(pattern_factory) 

259 

260 if not callable(pattern_factory): 

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

262 

263 if not _is_iterable(lines): 

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

265 

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

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

268 

269 @staticmethod 

270 def _make_backend( 

271 name: BackendNamesHint, 

272 patterns: Sequence[Pattern], 

273 ) -> _Backend: 

274 """ 

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

276 change. 

277 

278 Create the backend for the patterns. 

279 

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

281 

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

283 contains the compiled patterns. 

284 

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

286 """ 

287 return make_pathspec_backend(name, patterns) 

288 

289 def match_entries( 

290 self, 

291 entries: Iterable[TreeEntry], 

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

293 *, 

294 negate: Optional[bool] = None, 

295 ) -> Iterator[TreeEntry]: 

296 """ 

297 Matches the entries to this path-spec. 

298 

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

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

301 

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

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

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

305 

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

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

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

309 :data:`False`. 

310 

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

312 :class:`.TreeEntry`). 

313 """ 

314 if not _is_iterable(entries): 

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

316 

317 for entry in entries: 

318 norm_file = normalize_file(entry.path, separators) 

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

320 

321 if negate: 

322 include = not include 

323 

324 if include: 

325 yield entry 

326 

327 def match_file( 

328 self, 

329 file: StrPath, 

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

331 ) -> bool: 

332 """ 

333 Matches the file to this path-spec. 

334 

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

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

337 

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

339 optionally contains the path separators to normalize. See 

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

341 

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

343 """ 

344 norm_file = normalize_file(file, separators) 

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

346 return bool(include) 

347 

348 def match_files( 

349 self, 

350 files: Iterable[StrPath], 

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

352 *, 

353 negate: Optional[bool] = None, 

354 ) -> Iterator[StrPath]: 

355 """ 

356 Matches the files to this path-spec. 

357 

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

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

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

361 

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

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

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

365 

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

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

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

369 :data:`False`. 

370 

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

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

373 """ 

374 if not _is_iterable(files): 

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

376 

377 for orig_file in files: 

378 norm_file = normalize_file(orig_file, separators) 

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

380 

381 if negate: 

382 include = not include 

383 

384 if include: 

385 yield orig_file 

386 

387 def match_tree_entries( 

388 self, 

389 root: StrPath, 

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

391 follow_links: Optional[bool] = None, 

392 *, 

393 negate: Optional[bool] = None, 

394 ) -> Iterator[TreeEntry]: 

395 """ 

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

397 path-spec. 

398 

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

400 search. 

401 

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

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

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

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

406 

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

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

409 :data:`True`. 

410 

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

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

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

414 :data:`False`. 

415 

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

417 :class:`.TreeEntry`). 

418 """ 

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

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

421 

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

423 @deprecated(( 

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

425 )) 

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

427 """ 

428 .. version-deprecated:: 0.3.2 

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

430 method. 

431 """ 

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

433 

434 def match_tree_files( 

435 self, 

436 root: StrPath, 

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

438 follow_links: Optional[bool] = None, 

439 *, 

440 negate: Optional[bool] = None, 

441 ) -> Iterator[str]: 

442 """ 

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

444 path-spec. 

445 

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

447 search for files. 

448 

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

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

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

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

453 

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

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

456 :data:`True`. 

457 

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

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

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

461 :data:`False`. 

462 

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

464 """ 

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

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