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
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)
21Self = TypeVar("Self", bound='PathSpec')
22"""
23:class:`.PathSpec` self type hint to support Python v<3.11 using PEP 673
24recommendation.
25"""
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)
47class PathSpec(object):
48 """
49 The :class:`PathSpec` class is a wrapper around a list of compiled
50 :class:`.Pattern` instances.
51 """
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.
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`.
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)
75 if backend is None:
76 backend = 'best'
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)
84 self._backend: _Backend = use_backend
85 """
86 *_backend* (:class:`._Backend`) is the pattern (regular expression) matching
87 backend.
88 """
90 self._backend_name: BackendNamesHint = backend
91 """
92 *_backend_name* (:class:`str`) is the name of backend to use.
93 """
95 self.patterns: Sequence[Pattern] = patterns
96 """
97 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`)
98 contains the compiled patterns.
99 """
101 def __add__(self: Self, other: PathSpec) -> Self:
102 """
103 Combines the :attr:`self.patterns <.PathSpec.patterns>` patterns from two
104 :class:`PathSpec` instances.
105 """
106 if isinstance(other, PathSpec):
107 return self.__class__(self.patterns + other.patterns, backend=self._backend_name)
108 else:
109 return NotImplemented
111 def __eq__(self, other: object) -> bool:
112 """
113 Tests the equality of this path-spec with *other* (:class:`PathSpec`) by
114 comparing their :attr:`self.patterns <.PathSpec.patterns>` attributes.
115 """
116 if isinstance(other, PathSpec):
117 paired_patterns = zip_longest(self.patterns, other.patterns)
118 return all(a == b for a, b in paired_patterns)
119 else:
120 return NotImplemented
122 def __iadd__(self: Self, other: PathSpec) -> Self:
123 """
124 Adds the :attr:`self.patterns <.PathSpec.patterns>` from *other*
125 (:class:`PathSpec`) to this instance.
126 """
127 if isinstance(other, PathSpec):
128 self.patterns += other.patterns
129 self._backend = self._make_backend(self._backend_name, self.patterns)
130 return self
131 else:
132 return NotImplemented
134 def __len__(self) -> int:
135 """
136 Returns the number of :attr:`self.patterns <.PathSpec.patterns>` this
137 path-spec contains (:class:`int`).
138 """
139 return len(self.patterns)
141 def check_file(
142 self,
143 file: TStrPath,
144 separators: Optional[Collection[str]] = None,
145 ) -> CheckResult[TStrPath]:
146 """
147 Check the files against this path-spec.
149 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be matched
150 against :attr:`self.patterns <.PathSpec.patterns>`.
152 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
153 :data:`None`) optionally contains the path separators to normalize. See
154 :func:`.normalize_file` for more information.
156 Returns the file check result (:class:`.CheckResult`).
157 """
158 norm_file = normalize_file(file, separators)
159 include, index = self._backend.match_file(norm_file)
160 return CheckResult(file, include, index)
162 def check_files(
163 self,
164 files: Iterable[TStrPath],
165 separators: Optional[Collection[str]] = None,
166 ) -> Iterator[CheckResult[TStrPath]]:
167 """
168 Check the files against this path-spec.
170 *files* (:class:`~collections.abc.Iterable` of :class:`str` or
171 :class:`os.PathLike`) contains the file paths to be checked against
172 :attr:`self.patterns <.PathSpec.patterns>`.
174 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
175 :data:`None`) optionally contains the path separators to normalize. See
176 :func:`.normalize_file` for more information.
178 Returns an :class:`~collections.abc.Iterator` yielding each file check
179 result (:class:`.CheckResult`).
180 """
181 if not _is_iterable(files):
182 raise TypeError(f"files:{files!r} is not an iterable.")
184 for orig_file in files:
185 norm_file = normalize_file(orig_file, separators)
186 include, index = self._backend.match_file(norm_file)
187 yield CheckResult(orig_file, include, index)
189 def check_tree_files(
190 self,
191 root: StrPath,
192 on_error: Optional[Callable[[OSError], None]] = None,
193 follow_links: Optional[bool] = None,
194 ) -> Iterator[CheckResult[str]]:
195 """
196 Walks the specified root path for all files and checks them against this
197 path-spec.
199 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
200 search for files.
202 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
203 is the error handler for file-system exceptions. It will be called with the
204 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
205 is :data:`None` to ignore file-system exceptions.
207 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
208 symbolic links that resolve to directories. Default is :data:`None` for
209 :data:`True`.
211 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
212 results of the patterns. If :data:`True`, a pattern matching a file will
213 exclude the file rather than include it. Default is :data:`None` for
214 :data:`False`.
216 Returns an :class:`~collections.abc.Iterator` yielding each file check
217 result (:class:`.CheckResult`).
218 """
219 files = util.iter_tree_files(root, on_error=on_error, follow_links=follow_links)
220 yield from self.check_files(files)
222 @classmethod
223 def from_lines(
224 cls: type[Self],
225 pattern_factory: Union[str, Callable[[AnyStr], Pattern]],
226 lines: Iterable[AnyStr],
227 *,
228 backend: Union[BackendNamesHint, str, None] = None,
229 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], _Backend]] = None,
230 ) -> Self:
231 """
232 Compiles the pattern lines.
234 *pattern_factory* can be either the name of a registered pattern factory
235 (:class:`str`), or a :class:`~collections.abc.Callable` used to compile
236 patterns. It must accept an uncompiled pattern (:class:`str`) and return the
237 compiled pattern (:class:`.Pattern`).
239 *lines* (:class:`~collections.abc.Iterable`) yields each uncompiled pattern
240 (:class:`str`). This simply has to yield each line so that it can be a
241 :class:`io.TextIOBase` (e.g., from :func:`open` or :class:`io.StringIO`) or
242 the result from :meth:`str.splitlines`.
244 *backend* (:class:`str` or :data:`None`) is the pattern (or regular
245 expression) matching backend to use. Default is :data:`None` for "best" to
246 use the best available backend. Priority of backends is: "re2", "hyperscan",
247 "simple". The "simple" backend is always available.
249 Returns the :class:`PathSpec` instance.
250 """
251 if isinstance(pattern_factory, str):
252 pattern_factory = util.lookup_pattern(pattern_factory)
254 if not callable(pattern_factory):
255 raise TypeError(f"pattern_factory:{pattern_factory!r} is not callable.")
257 if not _is_iterable(lines):
258 raise TypeError(f"lines:{lines!r} is not an iterable.")
260 patterns = [pattern_factory(line) for line in lines if line]
261 return cls(patterns, backend=backend, _test_backend_factory=_test_backend_factory)
263 @staticmethod
264 def _make_backend(
265 name: BackendNamesHint,
266 patterns: Sequence[Pattern],
267 ) -> _Backend:
268 """
269 .. warning:: This method is not part of the public API. It is subject to
270 change.
272 Create the backend for the patterns.
274 *name* (:class:`str`) is the name of the backend.
276 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`)
277 contains the compiled patterns.
279 Returns the matcher (:class:`._Backend`).
280 """
281 return make_pathspec_backend(name, patterns)
283 def match_entries(
284 self,
285 entries: Iterable[TreeEntry],
286 separators: Optional[Collection[str]] = None,
287 *,
288 negate: Optional[bool] = None,
289 ) -> Iterator[TreeEntry]:
290 """
291 Matches the entries to this path-spec.
293 *entries* (:class:`~collections.abc.Iterable` of :class:`.TreeEntry`)
294 contains the entries to be matched against :attr:`self.patterns <.PathSpec.patterns>`.
296 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
297 :data:`None`) optionally contains the path separators to normalize. See
298 :func:`.normalize_file` for more information.
300 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
301 results of the patterns. If :data:`True`, a pattern matching a file will
302 exclude the file rather than include it. Default is :data:`None` for
303 :data:`False`.
305 Returns the matched entries (:class:`~collections.abc.Iterator` of
306 :class:`.TreeEntry`).
307 """
308 if not _is_iterable(entries):
309 raise TypeError(f"entries:{entries!r} is not an iterable.")
311 for entry in entries:
312 norm_file = normalize_file(entry.path, separators)
313 include, _index = self._backend.match_file(norm_file)
315 if negate:
316 include = not include
318 if include:
319 yield entry
321 def match_file(
322 self,
323 file: StrPath,
324 separators: Optional[Collection[str]] = None,
325 ) -> bool:
326 """
327 Matches the file to this path-spec.
329 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be matched
330 against :attr:`self.patterns <.PathSpec.patterns>`.
332 *separators* (:class:`~collections.abc.Collection` of :class:`str`)
333 optionally contains the path separators to normalize. See
334 :func:`.normalize_file` for more information.
336 Returns :data:`True` if *file* matched; otherwise, :data:`False`.
337 """
338 norm_file = normalize_file(file, separators)
339 include, _index = self._backend.match_file(norm_file)
340 return bool(include)
342 def match_files(
343 self,
344 files: Iterable[StrPath],
345 separators: Optional[Collection[str]] = None,
346 *,
347 negate: Optional[bool] = None,
348 ) -> Iterator[StrPath]:
349 """
350 Matches the files to this path-spec.
352 *files* (:class:`~collections.abc.Iterable` of :class:`str` or
353 :class:`os.PathLike`) contains the file paths to be matched against
354 :attr:`self.patterns <.PathSpec.patterns>`.
356 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
357 :data:`None`) optionally contains the path separators to normalize. See
358 :func:`.normalize_file` for more information.
360 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
361 results of the patterns. If :data:`True`, a pattern matching a file will
362 exclude the file rather than include it. Default is :data:`None` for
363 :data:`False`.
365 Returns the matched files (:class:`~collections.abc.Iterator` of
366 :class:`str` or :class:`os.PathLike`).
367 """
368 if not _is_iterable(files):
369 raise TypeError(f"files:{files!r} is not an iterable.")
371 for orig_file in files:
372 norm_file = normalize_file(orig_file, separators)
373 include, _index = self._backend.match_file(norm_file)
375 if negate:
376 include = not include
378 if include:
379 yield orig_file
381 def match_tree_entries(
382 self,
383 root: StrPath,
384 on_error: Optional[Callable[[OSError], None]] = None,
385 follow_links: Optional[bool] = None,
386 *,
387 negate: Optional[bool] = None,
388 ) -> Iterator[TreeEntry]:
389 """
390 Walks the specified root path for all files and matches them to this
391 path-spec.
393 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
394 search.
396 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
397 is the error handler for file-system exceptions. It will be called with the
398 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
399 is :data:`None` to ignore file-system exceptions.
401 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
402 symbolic links that resolve to directories. Default is :data:`None` for
403 :data:`True`.
405 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
406 results of the patterns. If :data:`True`, a pattern matching a file will
407 exclude the file rather than include it. Default is :data:`None` for
408 :data:`False`.
410 Returns the matched files (:class:`~collections.abc.Iterator` of
411 :class:`.TreeEntry`).
412 """
413 entries = util.iter_tree_entries(root, on_error=on_error, follow_links=follow_links)
414 yield from self.match_entries(entries, negate=negate)
416 # NOTICE: The deprecation warning was only added in 1.0.0 (from 2026-01-05).
417 @deprecated((
418 "PathSpec.match_tree() is deprecated. Use .match_tree_files() instead."
419 ))
420 def match_tree(self, *args, **kw) -> Iterator[str]:
421 """
422 .. version-deprecated:: 0.3.2
423 This is an alias for the :meth:`self.match_tree_files <.PathSpec.match_tree_files>`
424 method.
425 """
426 return self.match_tree_files(*args, **kw)
428 def match_tree_files(
429 self,
430 root: StrPath,
431 on_error: Optional[Callable[[OSError], None]] = None,
432 follow_links: Optional[bool] = None,
433 *,
434 negate: Optional[bool] = None,
435 ) -> Iterator[str]:
436 """
437 Walks the specified root path for all files and matches them to this
438 path-spec.
440 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
441 search for files.
443 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
444 is the error handler for file-system exceptions. It will be called with the
445 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
446 is :data:`None` to ignore file-system exceptions.
448 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
449 symbolic links that resolve to directories. Default is :data:`None` for
450 :data:`True`.
452 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
453 results of the patterns. If :data:`True`, a pattern matching a file will
454 exclude the file rather than include it. Default is :data:`None` for
455 :data:`False`.
457 Returns the matched files (:class:`~collections.abc.Iterable` of :class:`str`).
458 """
459 files = util.iter_tree_files(root, on_error=on_error, follow_links=follow_links)
460 yield from self.match_files(files, negate=negate)