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
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
1"""
2This module provides :class:`.PathSpec` which is an object-oriented interface
3for pattern matching of files.
4"""
5from __future__ import annotations
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)
24Self = TypeVar("Self", bound='PathSpec')
25"""
26:class:`.PathSpec` self type hint to support Python v<3.11 using PEP 673
27recommendation.
28"""
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)
55class PathSpec(Generic[TPattern_co]):
56 """
57 The :class:`PathSpec` class is a wrapper around a list of compiled
58 :class:`.Pattern` instances.
59 """
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.
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`.
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)
85 if backend is None:
86 backend = 'best'
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)
94 self._backend: _Backend = use_backend
95 """
96 *_backend* (:class:`._Backend`) is the pattern (regular expression) matching
97 backend.
98 """
100 self._backend_name: BackendNamesHint = backend_name
101 """
102 *_backend_name* (:class:`str`) is the name of backend to use.
103 """
105 self.patterns: Sequence[TPattern_co] = use_patterns
106 """
107 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`)
108 contains the compiled patterns.
109 """
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})"
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
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
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
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)
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.
165 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be matched
166 against :attr:`self.patterns <.PathSpec.patterns>`.
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.
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)
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.
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>`.
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.
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.")
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)
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.
215 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
216 search for files.
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.
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`.
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`.
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)
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 ...
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 ...
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 ...
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 ...
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.
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`).
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`.
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.
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.")
323 if not _is_iterable(lines):
324 raise TypeError(f"lines:{lines!r} is not an iterable.")
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
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.
339 Create the backend for the patterns.
341 *name* (:class:`str`) is the name of the backend.
343 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`)
344 contains the compiled patterns.
346 Returns the matcher (:class:`._Backend`).
347 """
348 return make_pathspec_backend(name, patterns)
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.
360 *entries* (:class:`~collections.abc.Iterable` of :class:`.TreeEntry`)
361 contains the entries to be matched against :attr:`self.patterns <.PathSpec.patterns>`.
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.
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`.
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.")
378 for entry in entries:
379 norm_file = normalize_file(entry.path, separators)
380 include, _index = self._backend.match_file(norm_file)
382 if negate:
383 include = not include
385 if include:
386 yield entry
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.
396 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be matched
397 against :attr:`self.patterns <.PathSpec.patterns>`.
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.
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)
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.
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>`.
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.
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`.
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.")
438 for orig_file in files:
439 norm_file = normalize_file(orig_file, separators)
440 include, _index = self._backend.match_file(norm_file)
442 if negate:
443 include = not include
445 if include:
446 yield orig_file
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.
460 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
461 search.
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.
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`.
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`.
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)
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)
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.
507 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
508 search for files.
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.
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`.
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`.
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)