1"""
2This module provides the simple backend for :class:`~pathspec.gitignore.GitIgnoreSpec`.
3
4WARNING: The *pathspec._backends.simple* package is not part of the public API.
5Its contents and structure are likely to change.
6"""
7
8from collections.abc import (
9 Sequence)
10from typing import (
11 Optional) # Replaced by `X | None` in 3.10.
12
13from ...pattern import (
14 RegexPattern)
15from ...patterns.gitwildmatch import (
16 _DIR_MARK)
17from ..._typing import (
18 override) # Added in 3.12.
19
20from .pathspec import (
21 SimplePsBackend)
22
23
24class SimpleGiBackend(SimplePsBackend):
25 """
26 The :class:`SimpleGiBackend` class is the default (or simple) implementation
27 used by :class:`~pathspec.gitignore.GitIgnoreSpec` for matching files.
28 """
29
30 # Change type hint.
31 _patterns: list[tuple[int, RegexPattern]]
32
33 def __init__(
34 self,
35 patterns: Sequence[RegexPattern],
36 *,
37 no_filter: Optional[bool] = None,
38 no_reverse: Optional[bool] = None,
39 ) -> None:
40 """
41 Initialize the :class:`SimpleGiBackend` instance.
42
43 *patterns* (:class:`Sequence` of :class:`.RegexPattern`) contains the
44 compiled patterns.
45
46 *no_filter* (:class:`bool`) is whether to keep no-op patterns (:data:`True`),
47 or remove them (:data:`False`).
48
49 *no_reverse* (:class:`bool`) is whether to keep the pattern order
50 (:data:`True`), or reverse the order (:data:`True`).
51 """
52 super().__init__(patterns, no_filter=no_filter, no_reverse=no_reverse)
53
54 @override
55 def match_file(self, file: str) -> tuple[Optional[bool], Optional[int]]:
56 """
57 Check the file against the patterns.
58
59 *file* (:class:`str`) is the normalized file path to check.
60
61 Returns a :class:`tuple` containing whether to include *file* (:class:`bool`
62 or :data:`None`), and the index of the last matched pattern (:class:`int` or
63 :data:`None`).
64 """
65 is_reversed = self._is_reversed
66
67 out_include: Optional[bool] = None
68 out_index: Optional[int] = None
69 out_priority = 0
70 for index, pattern in self._patterns:
71 if (
72 (include := pattern.include) is not None
73 and (match := pattern.match_file(file)) is not None
74 ):
75 # Pattern matched.
76
77 # Check for directory marker.
78 dir_mark = match.match.groupdict().get(_DIR_MARK)
79
80 if dir_mark:
81 # Pattern matched by a directory pattern.
82 priority = 1
83 else:
84 # Pattern matched by a file pattern.
85 priority = 2
86
87 if is_reversed:
88 if priority > out_priority:
89 out_include = include
90 out_index = index
91 out_priority = priority
92 else:
93 # Forward.
94 if (include and dir_mark) or priority >= out_priority:
95 out_include = include
96 out_index = index
97 out_priority = priority
98
99 if is_reversed and priority == 2:
100 # Patterns are being checked in reverse order. The first pattern that
101 # matches with priority 2 takes precedence.
102 break
103
104 return out_include, out_index