-
Notifications
You must be signed in to change notification settings - Fork 39
/
gitignore.py
138 lines (117 loc) · 3.8 KB
/
gitignore.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"""
This module provides :class:`.GitIgnoreSpec` which replicates
*.gitignore* behavior.
"""
from typing import (
AnyStr,
Callable,
Collection,
Iterable,
Type,
TypeVar,
Union)
from .pathspec import (
PathSpec)
from .pattern import (
Pattern)
from .patterns.gitwildmatch import (
GitWildMatchPattern,
GitWildMatchPatternError,
_DIR_MARK)
from .util import (
_is_iterable)
Self = TypeVar("Self", bound="GitIgnoreSpec")
"""
:class:`GitIgnoreSpec` self type hint to support Python v<3.11 using PEP
673 recommendation.
"""
class GitIgnoreSpec(PathSpec):
"""
The :class:`GitIgnoreSpec` class extends :class:`PathSpec` to
replicate *.gitignore* behavior.
"""
def __eq__(self, other: object) -> bool:
"""
Tests the equality of this gitignore-spec with *other*
(:class:`GitIgnoreSpec`) by comparing their :attr:`~PathSpec.patterns`
attributes. A non-:class:`GitIgnoreSpec` will not compare equal.
"""
if isinstance(other, GitIgnoreSpec):
return super().__eq__(other)
elif isinstance(other, PathSpec):
return False
else:
return NotImplemented
@classmethod
def from_lines(
cls: Type[Self],
lines: Iterable[AnyStr],
pattern_factory: Union[str, Callable[[AnyStr], Pattern], None] = None,
) -> Self:
"""
Compiles the pattern lines.
*lines* (:class:`~collections.abc.Iterable`) yields each uncompiled
pattern (:class:`str`). This simply has to yield each line so it can
be a :class:`io.TextIOBase` (e.g., from :func:`open` or
:class:`io.StringIO`) or the result from :meth:`str.splitlines`.
*pattern_factory* can be :data:`None`, the name of a registered
pattern factory (:class:`str`), or a :class:`~collections.abc.Callable`
used to compile patterns. The callable must accept an uncompiled
pattern (:class:`str`) and return the compiled pattern (:class:`.Pattern`).
Default is :data:`None` for :class:`.GitWildMatchPattern`).
Returns the :class:`GitIgnoreSpec` instance.
"""
if pattern_factory is None:
pattern_factory = GitWildMatchPattern
elif (isinstance(lines, str) or callable(lines)) and _is_iterable(pattern_factory):
# Support reversed order of arguments from PathSpec.
pattern_factory, lines = lines, pattern_factory
self = super().from_lines(pattern_factory, lines)
return self # type: ignore
@staticmethod
def _match_file(
patterns: Collection[GitWildMatchPattern],
file: str,
) -> bool:
"""
Matches the file to the patterns.
.. NOTE:: Subclasses of :class:`.PathSpec` may override this
method as an instance method. It does not have to be a static
method.
*patterns* (:class:`~collections.abc.Iterable` of :class:`~pathspec.pattern.Pattern`)
contains the patterns to use.
*file* (:class:`str`) is the normalized file path to be matched
against *patterns*.
Returns :data:`True` if *file* matched; otherwise, :data:`False`.
"""
out_matched = False
out_priority = 0
for pattern in patterns:
if pattern.include is not None:
match = pattern.match_file(file)
if match is not None:
# Pattern matched.
# Check for directory marker.
try:
dir_mark = match.match.group(_DIR_MARK)
except IndexError as e:
# NOTICE: The exact content of this error message is subject
# to change.
raise GitWildMatchPatternError((
f"Invalid git pattern: directory marker regex group is missing. "
f"Debug: file={file!r} regex={pattern.regex!r} "
f"group={_DIR_MARK!r} match={match.match!r}."
)) from e
if dir_mark:
# Pattern matched by a directory pattern.
priority = 1
else:
# Pattern matched by a file pattern.
priority = 2
if pattern.include and dir_mark:
out_matched = pattern.include
out_priority = priority
elif priority >= out_priority:
out_matched = pattern.include
out_priority = priority
return out_matched