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

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) 

20try: 

21 from typing import Self # Added in 3.11. 

22except ImportError: 

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

24 

25from pathspec import util 

26from pathspec.backend import ( 

27 _Backend, 

28 BackendNamesHint) 

29from pathspec._backends.agg import ( 

30 make_pathspec_backend) 

31from pathspec.pattern import ( 

32 Pattern) 

33from pathspec._typing import ( 

34 AnyStr, # Removed in 3.18. 

35 deprecated) # Added in 3.13. 

36from pathspec.util import ( 

37 CheckResult, 

38 StrPath, 

39 TStrPath, 

40 TreeEntry, 

41 _is_iterable, 

42 normalize_file) 

43 

44 

45class PathSpec(object): 

46 """ 

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

48 :class:`.Pattern` instances. 

49 """ 

50 

51 def __init__( 

52 self, 

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

54 *, 

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

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

57 ) -> None: 

58 """ 

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

60 

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

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

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

64 

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

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

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

68 The "simple" backend is always available. 

69 """ 

70 if not isinstance(patterns, Sequence): 

71 patterns = list(patterns) 

72 

73 if backend is None: 

74 backend = 'best' 

75 

76 backend = cast(BackendNamesHint, backend) 

77 if _test_backend_factory is not None: 

78 use_backend = _test_backend_factory(patterns) 

79 else: 

80 use_backend = self._make_backend(backend, patterns) 

81 

82 self._backend: _Backend = use_backend 

83 """ 

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

85 backend. 

86 """ 

87 

88 self._backend_name: BackendNamesHint = backend 

89 """ 

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

91 """ 

92 

93 self.patterns: Sequence[Pattern] = patterns 

94 """ 

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

96 contains the compiled patterns. 

97 """ 

98 

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

100 """ 

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

102 :class:`PathSpec` instances. 

103 """ 

104 if isinstance(other, PathSpec): 

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

106 else: 

107 return NotImplemented 

108 

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

110 """ 

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

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

113 """ 

114 if isinstance(other, PathSpec): 

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

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

117 else: 

118 return NotImplemented 

119 

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

121 """ 

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

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

124 """ 

125 if isinstance(other, PathSpec): 

126 self.patterns += other.patterns 

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

128 return self 

129 else: 

130 return NotImplemented 

131 

132 def __len__(self) -> int: 

133 """ 

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

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

136 """ 

137 return len(self.patterns) 

138 

139 def check_file( 

140 self, 

141 file: TStrPath, 

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

143 ) -> CheckResult[TStrPath]: 

144 """ 

145 Check the files against this path-spec. 

146 

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

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

149 

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

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

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

153 

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

155 """ 

156 norm_file = normalize_file(file, separators) 

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

158 return CheckResult(file, include, index) 

159 

160 def check_files( 

161 self, 

162 files: Iterable[TStrPath], 

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

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

165 """ 

166 Check the files against this path-spec. 

167 

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

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

170 :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 an :class:`~collections.abc.Iterator` yielding each file check 

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

178 """ 

179 if not _is_iterable(files): 

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

181 

182 for orig_file in files: 

183 norm_file = normalize_file(orig_file, separators) 

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

185 yield CheckResult(orig_file, include, index) 

186 

187 def check_tree_files( 

188 self, 

189 root: StrPath, 

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

191 follow_links: Optional[bool] = None, 

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

193 """ 

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

195 path-spec. 

196 

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

198 search for files. 

199 

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

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

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

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

204 

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

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

207 :data:`True`. 

208 

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

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

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

212 :data:`False`. 

213 

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

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

216 """ 

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

218 yield from self.check_files(files) 

219 

220 @classmethod 

221 def from_lines( 

222 cls: type[Self], 

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

224 lines: Iterable[AnyStr], 

225 *, 

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

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

228 ) -> Self: 

229 """ 

230 Compiles the pattern lines. 

231 

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

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

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

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

236 

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

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

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

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

241 

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

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

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

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

246 

247 Returns the :class:`PathSpec` instance. 

248 """ 

249 if isinstance(pattern_factory, str): 

250 pattern_factory = util.lookup_pattern(pattern_factory) 

251 

252 if not callable(pattern_factory): 

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

254 

255 if not _is_iterable(lines): 

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

257 

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

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

260 

261 @staticmethod 

262 def _make_backend( 

263 name: BackendNamesHint, 

264 patterns: Sequence[Pattern], 

265 ) -> _Backend: 

266 """ 

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

268 change. 

269 

270 Create the backend for the patterns. 

271 

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

273 

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

275 contains the compiled patterns. 

276 

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

278 """ 

279 return make_pathspec_backend(name, patterns) 

280 

281 def match_entries( 

282 self, 

283 entries: Iterable[TreeEntry], 

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

285 *, 

286 negate: Optional[bool] = None, 

287 ) -> Iterator[TreeEntry]: 

288 """ 

289 Matches the entries to this path-spec. 

290 

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

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

293 

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

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

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

297 

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

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

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

301 :data:`False`. 

302 

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

304 :class:`.TreeEntry`). 

305 """ 

306 if not _is_iterable(entries): 

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

308 

309 for entry in entries: 

310 norm_file = normalize_file(entry.path, separators) 

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

312 

313 if negate: 

314 include = not include 

315 

316 if include: 

317 yield entry 

318 

319 def match_file( 

320 self, 

321 file: StrPath, 

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

323 ) -> bool: 

324 """ 

325 Matches the file to this path-spec. 

326 

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

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

329 

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

331 optionally contains the path separators to normalize. See 

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

333 

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

335 """ 

336 norm_file = normalize_file(file, separators) 

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

338 return bool(include) 

339 

340 def match_files( 

341 self, 

342 files: Iterable[StrPath], 

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

344 *, 

345 negate: Optional[bool] = None, 

346 ) -> Iterator[StrPath]: 

347 """ 

348 Matches the files to this path-spec. 

349 

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

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

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

353 

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

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

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

357 

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

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

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

361 :data:`False`. 

362 

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

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

365 """ 

366 if not _is_iterable(files): 

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

368 

369 for orig_file in files: 

370 norm_file = normalize_file(orig_file, separators) 

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

372 

373 if negate: 

374 include = not include 

375 

376 if include: 

377 yield orig_file 

378 

379 def match_tree_entries( 

380 self, 

381 root: StrPath, 

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

383 follow_links: Optional[bool] = None, 

384 *, 

385 negate: Optional[bool] = None, 

386 ) -> Iterator[TreeEntry]: 

387 """ 

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

389 path-spec. 

390 

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

392 search. 

393 

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

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

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

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

398 

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

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

401 :data:`True`. 

402 

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

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

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

406 :data:`False`. 

407 

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

409 :class:`.TreeEntry`). 

410 """ 

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

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

413 

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

415 @deprecated(( 

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

417 )) 

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

419 """ 

420 .. version-deprecated:: 0.3.2 

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

422 method. 

423 """ 

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

425 

426 def match_tree_files( 

427 self, 

428 root: StrPath, 

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

430 follow_links: Optional[bool] = None, 

431 *, 

432 negate: Optional[bool] = None, 

433 ) -> Iterator[str]: 

434 """ 

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

436 path-spec. 

437 

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

439 search for files. 

440 

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

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

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

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

445 

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

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

448 :data:`True`. 

449 

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

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

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

453 :data:`False`. 

454 

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

456 """ 

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

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