127 lines
3.3 KiB
Python
127 lines
3.3 KiB
Python
"""
|
|
This module provides :class:`.GitIgnoreSpec` which replicates
|
|
*.gitignore* behavior.
|
|
"""
|
|
|
|
from typing import (
|
|
AnyStr,
|
|
Callable,
|
|
Collection,
|
|
Iterable,
|
|
TYPE_CHECKING,
|
|
Union)
|
|
|
|
from .pathspec import (
|
|
PathSpec)
|
|
from .pattern import (
|
|
Pattern)
|
|
from .patterns.gitwildmatch import (
|
|
GitWildMatchPattern)
|
|
from .util import (
|
|
_is_iterable)
|
|
|
|
|
|
class GitIgnoreSpec(PathSpec):
|
|
"""
|
|
The :class:`GitIgnoreSpec` class extends :class:`PathSpec` to
|
|
replicate *.gitignore* behavior.
|
|
"""
|
|
|
|
def __eq__(self, other: 'Self') -> 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,
|
|
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.
|
|
dir_mark = match.match.group('ps_d')
|
|
if dir_mark:
|
|
# Pattern matched by a directory pattern.
|
|
priority = 1
|
|
else:
|
|
# Pattern matched by a file pattern.
|
|
priority = 2
|
|
|
|
if priority >= out_priority:
|
|
out_matched = pattern.include
|
|
out_priority = priority
|
|
|
|
return out_matched
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
try:
|
|
from typing import Self
|
|
except ImportError:
|
|
try:
|
|
from typing_extensions import Self
|
|
except ImportError:
|
|
Self = GitIgnoreSpec
|