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
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 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')
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)
45class PathSpec(object):
46 """
47 The :class:`PathSpec` class is a wrapper around a list of compiled
48 :class:`.Pattern` instances.
49 """
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.
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`.
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)
73 if backend is None:
74 backend = 'best'
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)
82 self._backend: _Backend = use_backend
83 """
84 *_backend* (:class:`._Backend`) is the pattern (regular expression) matching
85 backend.
86 """
88 self._backend_name: BackendNamesHint = backend
89 """
90 *_backend_name* (:class:`str`) is the name of backend to use.
91 """
93 self.patterns: Sequence[Pattern] = patterns
94 """
95 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`)
96 contains the compiled patterns.
97 """
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
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
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
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)
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.
147 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be matched
148 against :attr:`self.patterns <.PathSpec.patterns>`.
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.
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)
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.
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>`.
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.
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.")
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)
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.
197 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
198 search for files.
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.
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`.
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`.
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)
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.
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`).
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`.
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.
247 Returns the :class:`PathSpec` instance.
248 """
249 if isinstance(pattern_factory, str):
250 pattern_factory = util.lookup_pattern(pattern_factory)
252 if not callable(pattern_factory):
253 raise TypeError(f"pattern_factory:{pattern_factory!r} is not callable.")
255 if not _is_iterable(lines):
256 raise TypeError(f"lines:{lines!r} is not an iterable.")
258 patterns = [pattern_factory(line) for line in lines if line]
259 return cls(patterns, backend=backend, _test_backend_factory=_test_backend_factory)
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.
270 Create the backend for the patterns.
272 *name* (:class:`str`) is the name of the backend.
274 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`)
275 contains the compiled patterns.
277 Returns the matcher (:class:`._Backend`).
278 """
279 return make_pathspec_backend(name, patterns)
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.
291 *entries* (:class:`~collections.abc.Iterable` of :class:`.TreeEntry`)
292 contains the entries to be matched against :attr:`self.patterns <.PathSpec.patterns>`.
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.
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`.
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.")
309 for entry in entries:
310 norm_file = normalize_file(entry.path, separators)
311 include, _index = self._backend.match_file(norm_file)
313 if negate:
314 include = not include
316 if include:
317 yield entry
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.
327 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be matched
328 against :attr:`self.patterns <.PathSpec.patterns>`.
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.
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)
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.
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>`.
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.
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`.
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.")
369 for orig_file in files:
370 norm_file = normalize_file(orig_file, separators)
371 include, _index = self._backend.match_file(norm_file)
373 if negate:
374 include = not include
376 if include:
377 yield orig_file
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.
391 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
392 search.
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.
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`.
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`.
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)
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)
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.
438 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
439 search for files.
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.
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`.
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`.
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)