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 an object-oriented interface for pattern matching of files.
3"""
5from collections.abc import (
6 Callable,
7 Collection,
8 Iterable,
9 Iterator,
10 Sequence)
11from itertools import (
12 zip_longest)
13from typing import (
14 AnyStr,
15 Optional, # Replaced by `X | None` in 3.10.
16 TypeVar,
17 Union, # Replaced by `X | Y` in 3.10.
18 cast)
20from . import util
21from ._backends.base import (
22 Backend,
23 BackendNamesHint)
24from ._backends.agg import (
25 make_pathspec_backend)
26from .pattern import (
27 Pattern)
28from .util import (
29 CheckResult,
30 StrPath,
31 TStrPath,
32 TreeEntry,
33 _is_iterable,
34 normalize_file)
36Self = TypeVar("Self", bound="PathSpec")
37"""
38:class:`PathSpec` self type hint to support Python v<3.11 using PEP 673
39recommendation.
40"""
43class PathSpec(object):
44 """
45 The :class:`PathSpec` class is a wrapper around a list of compiled
46 :class:`.Pattern` instances.
47 """
49 def __init__(
50 self,
51 patterns: Union[Sequence[Pattern], Iterable[Pattern]],
52 *,
53 backend: Union[BackendNamesHint, str, None] = None,
54 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], Backend]] = None,
55 ) -> None:
56 """
57 Initializes the :class:`PathSpec` instance.
59 *patterns* (:class:`~collections.abc.Sequence` or :class:`~collections.abc.Iterable`)
60 contains each compiled pattern (:class:`.Pattern`). If not a sequence, it
61 will be converted to a :class:`list`.
63 *backend* (:class:`str` or :data:`None`) is the pattern (or regex) matching
64 backend to use. Default is :data:`None` for "best" to use the best available
65 backend. Priority of backends is: "hyperscan", "simple". The "simple"
66 backend is always available.
67 """
68 if not isinstance(patterns, Sequence):
69 patterns = list(patterns)
71 if backend is None:
72 backend = 'best'
74 backend = cast(BackendNamesHint, backend)
75 if _test_backend_factory is not None:
76 use_backend = _test_backend_factory(patterns)
77 else:
78 use_backend = self._make_backend(backend, patterns)
80 self._backend: Backend = use_backend
81 """
82 *_backend* (:class:`.Backend`) is the pattern (or regex) matching backend.
83 """
85 self._backend_name: BackendNamesHint = backend
86 """
87 *_backend_name* (:class:`str`) is the name of backend to use.
88 """
90 self.patterns: Sequence[Pattern] = patterns
91 """
92 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`)
93 contains the compiled patterns.
94 """
96 def __add__(self: Self, other: "PathSpec") -> Self:
97 """
98 Combines the :attr:`Pathspec.patterns` patterns from two :class:`PathSpec`
99 instances.
100 """
101 if isinstance(other, PathSpec):
102 return self.__class__(self.patterns + other.patterns, backend=self._backend_name)
103 else:
104 return NotImplemented
106 def __eq__(self, other: object) -> bool:
107 """
108 Tests the equality of this path-spec with *other* (:class:`PathSpec`) by
109 comparing their :attr:`~PathSpec.patterns` attributes.
110 """
111 if isinstance(other, PathSpec):
112 paired_patterns = zip_longest(self.patterns, other.patterns)
113 return all(a == b for a, b in paired_patterns)
114 else:
115 return NotImplemented
117 def __iadd__(self: Self, other: "PathSpec") -> Self:
118 """
119 Adds the :attr:`Pathspec.patterns` patterns from one :class:`PathSpec`
120 instance to this instance.
121 """
122 if isinstance(other, PathSpec):
123 self.patterns += other.patterns
124 self._backend = self._make_backend(self._backend_name, self.patterns)
125 return self
126 else:
127 return NotImplemented
129 def __len__(self) -> int:
130 """
131 Returns the number of compiled patterns this path-spec contains (:class:`int`).
132 """
133 return len(self.patterns)
135 def check_file(
136 self,
137 file: TStrPath,
138 separators: Optional[Collection[str]] = None,
139 ) -> CheckResult[TStrPath]:
140 """
141 Check the files against this path-spec.
143 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be matched
144 against :attr:`self.patterns <PathSpec.patterns>`.
146 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
147 :data:`None`) optionally contains the path separators to normalize. See
148 :func:`~pathspec.util.normalize_file` for more information.
150 Returns the file check result (:class:`~pathspec.util.CheckResult`).
151 """
152 norm_file = normalize_file(file, separators)
153 include, index = self._backend.match_file(norm_file)
154 return CheckResult(file, include, index)
156 def check_files(
157 self,
158 files: Iterable[TStrPath],
159 separators: Optional[Collection[str]] = None,
160 ) -> Iterator[CheckResult[TStrPath]]:
161 """
162 Check the files against this path-spec.
164 *files* (:class:`~collections.abc.Iterable` of :class:`str` or
165 :class:`os.PathLike`) contains the file paths to be checked against
166 :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:`~pathspec.util.normalize_file` for more information.
172 Returns an :class:`~collections.abc.Iterator` yielding each file check
173 result (:class:`~pathspec.util.CheckResult`).
174 """
175 if not _is_iterable(files):
176 raise TypeError(f"files:{files!r} is not an iterable.")
178 for orig_file in files:
179 norm_file = normalize_file(orig_file, separators)
180 include, index = self._backend.match_file(norm_file)
181 yield CheckResult(orig_file, include, index)
183 def check_tree_files(
184 self,
185 root: StrPath,
186 on_error: Optional[Callable[[OSError], None]] = None,
187 follow_links: Optional[bool] = None,
188 ) -> Iterator[CheckResult[str]]:
189 """
190 Walks the specified root path for all files and checks them against this
191 path-spec.
193 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
194 search for files.
196 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
197 is the error handler for file-system exceptions. It will be called with the
198 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
199 is :data:`None` to ignore file-system exceptions.
201 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
202 symbolic links that resolve to directories. Default is :data:`None` for
203 :data:`True`.
205 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
206 results of the patterns. If :data:`True`, a pattern matching a file will
207 exclude the file rather than include it. Default is :data:`None` for
208 :data:`False`.
210 Returns an :class:`~collections.abc.Iterator` yielding each file check
211 result (:class:`~pathspec.util.CheckResult`).
212 """
213 files = util.iter_tree_files(root, on_error=on_error, follow_links=follow_links)
214 yield from self.check_files(files)
216 @classmethod
217 def from_lines(
218 cls: type[Self],
219 pattern_factory: Union[str, Callable[[AnyStr], Pattern]],
220 lines: Iterable[AnyStr],
221 *,
222 backend: Union[BackendNamesHint, str, None] = None,
223 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], Backend]] = None,
224 ) -> Self:
225 """
226 Compiles the pattern lines.
228 *pattern_factory* can be either the name of a registered pattern factory
229 (:class:`str`), or a :class:`~collections.abc.Callable` used to compile
230 patterns. It must accept an uncompiled pattern (:class:`str`) and return the
231 compiled pattern (:class:`.Pattern`).
233 *lines* (:class:`~collections.abc.Iterable`) yields each uncompiled pattern
234 (:class:`str`). This simply has to yield each line so that it can be a
235 :class:`io.TextIOBase` (e.g., from :func:`open` or :class:`io.StringIO`) or
236 the result from :meth:`str.splitlines`.
238 *backend* (:class:`str` or :data:`None`) is the pattern (or regex) matching
239 backend to use. Default is :data:`None` for "best" to use the best available
240 backend. Priority of backends is: "hyperscan", "simple". The "simple"
241 backend is always available.
243 Returns the :class:`PathSpec` instance.
244 """
245 if isinstance(pattern_factory, str):
246 pattern_factory = util.lookup_pattern(pattern_factory)
248 if not callable(pattern_factory):
249 raise TypeError(f"pattern_factory:{pattern_factory!r} is not callable.")
251 if not _is_iterable(lines):
252 raise TypeError(f"lines:{lines!r} is not an iterable.")
254 patterns = [pattern_factory(line) for line in lines if line]
255 return cls(patterns, backend=backend, _test_backend_factory=_test_backend_factory)
257 @staticmethod
258 def _make_backend(
259 name: BackendNamesHint,
260 patterns: Sequence[Pattern],
261 ) -> Backend:
262 """
263 Create the backend for the patterns.
265 *name* (:class:`str`) is the name of the backend.
267 *patterns* (:class:`.Sequence` of :class:`.Pattern`) contains the compiled
268 patterns.
270 Returns the matcher (:class:`.Backend`).
271 """
272 return make_pathspec_backend(name, patterns)
274 def match_entries(
275 self,
276 entries: Iterable[TreeEntry],
277 separators: Optional[Collection[str]] = None,
278 *,
279 negate: Optional[bool] = None,
280 ) -> Iterator[TreeEntry]:
281 """
282 Matches the entries to this path-spec.
284 *entries* (:class:`~collections.abc.Iterable` of :class:`~pathspec.util.TreeEntry`)
285 contains the entries to be matched against :attr:`self.patterns <PathSpec.patterns>`.
287 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
288 :data:`None`) optionally contains the path separators to normalize. See
289 :func:`~pathspec.util.normalize_file` for more information.
291 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
292 results of the patterns. If :data:`True`, a pattern matching a file will
293 exclude the file rather than include it. Default is :data:`None` for
294 :data:`False`.
296 Returns the matched entries (:class:`~collections.abc.Iterator` of
297 :class:`~pathspec.util.TreeEntry`).
298 """
299 if not _is_iterable(entries):
300 raise TypeError(f"entries:{entries!r} is not an iterable.")
302 for entry in entries:
303 norm_file = normalize_file(entry.path, separators)
304 include, _index = self._backend.match_file(norm_file)
306 if negate:
307 include = not include
309 if include:
310 yield entry
312 def match_file(
313 self,
314 file: StrPath,
315 separators: Optional[Collection[str]] = None,
316 ) -> bool:
317 """
318 Matches the file to this path-spec.
320 *file* (:class:`str` or :class:`os.PathLike`) is the file path to be matched
321 against :attr:`self.patterns <PathSpec.patterns>`.
323 *separators* (:class:`~collections.abc.Collection` of :class:`str`)
324 optionally contains the path separators to normalize. See
325 :func:`~pathspec.util.normalize_file` for more information.
327 Returns :data:`True` if *file* matched; otherwise, :data:`False`.
328 """
329 norm_file = normalize_file(file, separators)
330 include, _index = self._backend.match_file(norm_file)
331 return bool(include)
333 def match_files(
334 self,
335 files: Iterable[StrPath],
336 separators: Optional[Collection[str]] = None,
337 *,
338 negate: Optional[bool] = None,
339 ) -> Iterator[StrPath]:
340 """
341 Matches the files to this path-spec.
343 *files* (:class:`~collections.abc.Iterable` of :class:`str` or
344 :class:`os.PathLike`) contains the file paths to be matched against
345 :attr:`self.patterns <PathSpec.patterns>`.
347 *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
348 :data:`None`) optionally contains the path separators to normalize. See
349 :func:`~pathspec.util.normalize_file` for more information.
351 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
352 results of the patterns. If :data:`True`, a pattern matching a file will
353 exclude the file rather than include it. Default is :data:`None` for
354 :data:`False`.
356 Returns the matched files (:class:`~collections.abc.Iterator` of
357 :class:`str` or :class:`os.PathLike`).
358 """
359 if not _is_iterable(files):
360 raise TypeError(f"files:{files!r} is not an iterable.")
362 for orig_file in files:
363 norm_file = normalize_file(orig_file, separators)
364 include, _index = self._backend.match_file(norm_file)
366 if negate:
367 include = not include
369 if include:
370 yield orig_file
372 def match_tree_entries(
373 self,
374 root: StrPath,
375 on_error: Optional[Callable[[OSError], None]] = None,
376 follow_links: Optional[bool] = None,
377 *,
378 negate: Optional[bool] = None,
379 ) -> Iterator[TreeEntry]:
380 """
381 Walks the specified root path for all files and matches them to this
382 path-spec.
384 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
385 search.
387 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
388 is the error handler for file-system exceptions. It will be called with the
389 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
390 is :data:`None` to ignore file-system exceptions.
392 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
393 symbolic links that resolve to directories. Default is :data:`None` for
394 :data:`True`.
396 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
397 results of the patterns. If :data:`True`, a pattern matching a file will
398 exclude the file rather than include it. Default is :data:`None` for
399 :data:`False`.
401 Returns the matched files (:class:`~collections.abc.Iterator` of
402 :class:`.TreeEntry`).
403 """
404 entries = util.iter_tree_entries(root, on_error=on_error, follow_links=follow_links)
405 yield from self.match_entries(entries, negate=negate)
407 def match_tree_files(
408 self,
409 root: StrPath,
410 on_error: Optional[Callable[[OSError], None]] = None,
411 follow_links: Optional[bool] = None,
412 *,
413 negate: Optional[bool] = None,
414 ) -> Iterator[str]:
415 """
416 Walks the specified root path for all files and matches them to this
417 path-spec.
419 *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
420 search for files.
422 *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
423 is the error handler for file-system exceptions. It will be called with the
424 exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
425 is :data:`None` to ignore file-system exceptions.
427 *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
428 symbolic links that resolve to directories. Default is :data:`None` for
429 :data:`True`.
431 *negate* (:class:`bool` or :data:`None`) is whether to negate the match
432 results of the patterns. If :data:`True`, a pattern matching a file will
433 exclude the file rather than include it. Default is :data:`None` for
434 :data:`False`.
436 Returns the matched files (:class:`~collections.abc.Iterable` of :class:`str`).
437 """
438 files = util.iter_tree_files(root, on_error=on_error, follow_links=follow_links)
439 yield from self.match_files(files, negate=negate)
441 # Alias `match_tree_files()` as `match_tree()` for backward compatibility
442 # before v0.3.2.
443 match_tree = match_tree_files