Dulwich.io dulwich / 06bfbd9
Add typing. Jelmer Vernooń≥ 9 days ago
1 changed file(s) with 57 addition(s) and 40 deletion(s). Raw diff Collapse all Expand all
2323
2424 import os.path
2525 import re
26
27 from dulwich.config import get_xdg_config_home_path
28
29
30 def _translate_segment(segment):
26 from typing import (
27 BinaryIO,
28 Iterable,
29 List,
30 Optional,
31 TYPE_CHECKING,
32 Dict,
33 Union,
34 )
35
36 if TYPE_CHECKING:
37 from dulwich.repo import Repo
38
39 from dulwich.config import get_xdg_config_home_path, Config
40
41
42 def _translate_segment(segment: bytes) -> bytes:
3143 if segment == b"*":
3244 return b'[^/]+'
3345 res = b""
6274 return res
6375
6476
65 def translate(pat):
77 def translate(pat: bytes) -> bytes:
6678 """Translate a shell PATTERN to a regular expression.
6779
6880 There is no way to quote meta-characters.
99111 return res + b'\\Z'
100112
101113
102 def read_ignore_patterns(f):
114 def read_ignore_patterns(f: BinaryIO) -> Iterable[bytes]:
103115 """Read a git ignore file.
104116
105117 Args:
126138 yield line
127139
128140
129 def match_pattern(path, pattern, ignorecase=False):
141 def match_pattern(
142 path: bytes, pattern: bytes, ignorecase: bool = False) -> bool:
130143 """Match a gitignore-style pattern against a path.
131144
132145 Args:
142155 class Pattern(object):
143156 """A single ignore pattern."""
144157
145 def __init__(self, pattern, ignorecase=False):
158 def __init__(self, pattern: bytes, ignorecase: bool = False):
146159 self.pattern = pattern
147160 self.ignorecase = ignorecase
148161 if pattern[0:1] == b'!':
157170 flags = re.IGNORECASE
158171 self._re = re.compile(translate(pattern), flags)
159172
160 def __bytes__(self):
173 def __bytes__(self) -> bytes:
161174 return self.pattern
162175
163 def __str__(self):
176 def __str__(self) -> str:
164177 return os.fsdecode(self.pattern)
165178
166 def __eq__(self, other):
167 return (type(self) == type(other) and
179 def __eq__(self, other: object) -> bool:
180 return (isinstance(other, type(self)) and
168181 self.pattern == other.pattern and
169182 self.ignorecase == other.ignorecase)
170183
171 def __repr__(self):
172 return "%s(%s, %r)" % (
184 def __repr__(self) -> str:
185 return "%s(%r, %r)" % (
173186 type(self).__name__, self.pattern, self.ignorecase)
174187
175 def match(self, path):
188 def match(self, path: bytes) -> bool:
176189 """Try to match a path against this ignore pattern.
177190
178191 Args:
184197
185198 class IgnoreFilter(object):
186199
187 def __init__(self, patterns, ignorecase=False):
188 self._patterns = []
200 def __init__(self, patterns: Iterable[bytes], ignorecase: bool = False,
201 path=None):
202 self._patterns = [] # type: List[Pattern]
189203 self._ignorecase = ignorecase
204 self._path = path
190205 for pattern in patterns:
191206 self.append_pattern(pattern)
192207
193 def append_pattern(self, pattern):
208 def append_pattern(self, pattern: bytes) -> None:
194209 """Add a pattern to the set."""
195210 self._patterns.append(Pattern(pattern, self._ignorecase))
196211
197 def find_matching(self, path):
212 def find_matching(self, path: Union[bytes, str]) -> Iterable[Pattern]:
198213 """Yield all matching patterns for path.
199214
200215 Args:
201216 path: Path to match
202217 Returns:
203 Iterator over iterators
218 Iterator over iterators
204219 """
205220 if not isinstance(path, bytes):
206221 path = os.fsencode(path)
208223 if pattern.match(path):
209224 yield pattern
210225
211 def is_ignored(self, path):
226 def is_ignored(self, path: bytes) -> Optional[bool]:
212227 """Check whether a path is ignored.
213228
214229 For directories, include a trailing slash.
222237 return status
223238
224239 @classmethod
225 def from_path(cls, path, ignorecase=False):
240 def from_path(cls, path, ignorecase: bool = False) -> 'IgnoreFilter':
226241 with open(path, 'rb') as f:
227 ret = cls(read_ignore_patterns(f), ignorecase)
228 ret._path = path
229 return ret
230
231 def __repr__(self):
232 if getattr(self, '_path', None) is None:
242 return cls(read_ignore_patterns(f), ignorecase, path=path)
243
244 def __repr__(self) -> str:
245 path = getattr(self, '_path', None)
246 if path is not None:
247 return "%s.from_path(%r)" % (
248 type(self).__name__, path)
249 else:
233250 return "<%s>" % (type(self).__name__)
234 else:
235 return "%s.from_path(%r)" % (type(self).__name__, self._path)
236251
237252
238253 class IgnoreFilterStack(object):
241256 def __init__(self, filters):
242257 self._filters = filters
243258
244 def is_ignored(self, path):
259 def is_ignored(self, path: str) -> Optional[bool]:
245260 """Check whether a path is explicitly included or excluded in ignores.
246261
247262 Args:
258273 return status
259274
260275
261 def default_user_ignore_filter_path(config):
276 def default_user_ignore_filter_path(config: Config) -> str:
262277 """Return default user ignore filter path.
263278
264279 Args:
277292 class IgnoreFilterManager(object):
278293 """Ignore file manager."""
279294
280 def __init__(self, top_path, global_filters, ignorecase):
281 self._path_filters = {}
295 def __init__(
296 self, top_path: str, global_filters: List[IgnoreFilter],
297 ignorecase: bool):
298 self._path_filters = {} # type: Dict[str, Optional[IgnoreFilter]]
282299 self._top_path = top_path
283300 self._global_filters = global_filters
284301 self._ignorecase = ignorecase
285302
286 def __repr__(self):
303 def __repr__(self) -> str:
287304 return "%s(%s, %r, %r)" % (
288305 type(self).__name__, self._top_path,
289306 self._global_filters,
290307 self._ignorecase)
291308
292 def _load_path(self, path):
309 def _load_path(self, path: str) -> Optional[IgnoreFilter]:
293310 try:
294311 return self._path_filters[path]
295312 except KeyError:
303320 self._path_filters[path] = None
304321 return self._path_filters[path]
305322
306 def find_matching(self, path):
323 def find_matching(self, path: str) -> Iterable[Pattern]:
307324 """Find matching patterns for path.
308325
309326 Stops after the first ignore file with matches.
335352 filters.insert(0, (i, ignore_filter))
336353 return iter([])
337354
338 def is_ignored(self, path):
355 def is_ignored(self, path: str) -> Optional[bool]:
339356 """Check whether a path is explicitly included or excluded in ignores.
340357
341358 Args:
350367 return None
351368
352369 @classmethod
353 def from_repo(cls, repo):
370 def from_repo(cls, repo: 'Repo') -> 'IgnoreFilterManager':
354371 """Create a IgnoreFilterManager from a repository.
355372
356373 Args: