| # encoding: utf-8 |
| # |
| # This is a copy of source code from Pathspec 0.5.9 |
| # (https://pypi.org/project/pathspec/) which is |
| # available under an Mozilla Public License 2.0 |
| # (https://www.mozilla.org/en-US/MPL/2.0/). |
| # A copy of the license is also available in |
| # ../../licenses/LICENSE-pathspec.txt. |
| # |
| """ |
| This module provides the base definition for patterns. |
| """ |
| |
| import re |
| |
| from compat import unicode |
| |
| |
| class Pattern(object): |
| """ |
| The :class:`Pattern` class is the abstract definition of a pattern. |
| """ |
| |
| # Make the class dict-less. |
| __slots__ = ('include',) |
| |
| def __init__(self, include): |
| """ |
| Initializes the :class:`Pattern` instance. |
| |
| *include* (:class:`bool` or :data:`None`) is whether the matched |
| files should be included (:data:`True`), excluded (:data:`False`), |
| or is a null-operation (:data:`None`). |
| """ |
| |
| self.include = include |
| """ |
| *include* (:class:`bool` or :data:`None`) is whether the matched |
| files should be included (:data:`True`), excluded (:data:`False`), |
| or is a null-operation (:data:`None`). |
| """ |
| |
| def match(self, files): |
| """ |
| Matches this pattern against the specified files. |
| |
| *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains |
| each file relative to the root directory (e.g., ``"relative/path/to/file"``). |
| |
| Returns an :class:`~collections.abc.Iterable` yielding each matched |
| file path (:class:`str`). |
| """ |
| raise NotImplementedError("{}.{} must override match().".format(self.__class__.__module__, self.__class__.__name__)) |
| |
| |
| class RegexPattern(Pattern): |
| """ |
| The :class:`RegexPattern` class is an implementation of a pattern |
| using regular expressions. |
| """ |
| |
| # Make the class dict-less. |
| __slots__ = ('regex',) |
| |
| def __init__(self, pattern, include=None): |
| """ |
| Initializes the :class:`RegexPattern` instance. |
| |
| *pattern* (:class:`unicode`, :class:`bytes`, :class:`re.RegexObject`, |
| or :data:`None`) is the pattern to compile into a regular |
| expression. |
| |
| *include* (:class:`bool` or :data:`None`) must be :data:`None` |
| unless *pattern* is a precompiled regular expression (:class:`re.RegexObject`) |
| in which case it is whether matched files should be included |
| (:data:`True`), excluded (:data:`False`), or is a null operation |
| (:data:`None`). |
| |
| .. NOTE:: Subclasses do not need to support the *include* |
| parameter. |
| """ |
| |
| self.regex = None |
| """ |
| *regex* (:class:`re.RegexObject`) is the regular expression for the |
| pattern. |
| """ |
| |
| if isinstance(pattern, (unicode, bytes)): |
| assert include is None, "include:{!r} must be null when pattern:{!r} is a string.".format(include, pattern) |
| regex, include = self.pattern_to_regex(pattern) |
| # NOTE: Make sure to allow a null regular expression to be |
| # returned for a null-operation. |
| if include is not None: |
| regex = re.compile(regex) |
| |
| elif pattern is not None and hasattr(pattern, 'match'): |
| # Assume pattern is a precompiled regular expression. |
| # - NOTE: Used specified *include*. |
| regex = pattern |
| |
| elif pattern is None: |
| # NOTE: Make sure to allow a null pattern to be passed for a |
| # null-operation. |
| assert include is None, "include:{!r} must be null when pattern:{!r} is null.".format(include, pattern) |
| |
| else: |
| raise TypeError("pattern:{!r} is not a string, RegexObject, or None.".format(pattern)) |
| |
| super(RegexPattern, self).__init__(include) |
| self.regex = regex |
| |
| def __eq__(self, other): |
| """ |
| Tests the equality of this regex pattern with *other* (:class:`RegexPattern`) |
| by comparing their :attr:`~Pattern.include` and :attr:`~RegexPattern.regex` |
| attributes. |
| """ |
| if isinstance(other, RegexPattern): |
| return self.include == other.include and self.regex == other.regex |
| else: |
| return NotImplemented |
| |
| def match(self, files): |
| """ |
| Matches this pattern against the specified files. |
| |
| *files* (:class:`~collections.abc.Iterable` of :class:`str`) |
| contains each file relative to the root directory (e.g., "relative/path/to/file"). |
| |
| Returns an :class:`~collections.abc.Iterable` yielding each matched |
| file path (:class:`str`). |
| """ |
| if self.include is not None: |
| for path in files: |
| if self.regex.match(path) is not None: |
| yield path |
| |
| @classmethod |
| def pattern_to_regex(cls, pattern): |
| """ |
| Convert the pattern into an uncompiled regular expression. |
| |
| *pattern* (:class:`str`) is the pattern to convert into a regular |
| expression. |
| |
| Returns the uncompiled regular expression (:class:`str` or :data:`None`), |
| and whether matched files should be included (:data:`True`), |
| excluded (:data:`False`), or is a null-operation (:data:`None`). |
| |
| .. NOTE:: The default implementation simply returns *pattern* and |
| :data:`True`. |
| """ |
| return pattern, True |
| |