pattern.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # encoding: utf-8
  2. """
  3. This module provides the base definition for patterns.
  4. """
  5. import re
  6. from .compat import unicode
  7. class Pattern(object):
  8. """
  9. The :class:`Pattern` class is the abstract definition of a pattern.
  10. """
  11. # Make the class dict-less.
  12. __slots__ = ('include',)
  13. def __init__(self, include):
  14. """
  15. Initializes the :class:`Pattern` instance.
  16. *include* (:class:`bool` or :data:`None`) is whether the matched
  17. files should be included (:data:`True`), excluded (:data:`False`),
  18. or is a null-operation (:data:`None`).
  19. """
  20. self.include = include
  21. """
  22. *include* (:class:`bool` or :data:`None`) is whether the matched
  23. files should be included (:data:`True`), excluded (:data:`False`),
  24. or is a null-operation (:data:`None`).
  25. """
  26. def match(self, files):
  27. """
  28. Matches this pattern against the specified files.
  29. *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains
  30. each file relative to the root directory (e.g., ``"relative/path/to/file"``).
  31. Returns an :class:`~collections.abc.Iterable` yielding each matched
  32. file path (:class:`str`).
  33. """
  34. raise NotImplementedError("{}.{} must override match().".format(self.__class__.__module__, self.__class__.__name__))
  35. class RegexPattern(Pattern):
  36. """
  37. The :class:`RegexPattern` class is an implementation of a pattern
  38. using regular expressions.
  39. """
  40. # Make the class dict-less.
  41. __slots__ = ('regex',)
  42. def __init__(self, pattern, include=None):
  43. """
  44. Initializes the :class:`RegexPattern` instance.
  45. *pattern* (:class:`unicode`, :class:`bytes`, :class:`re.RegexObject`,
  46. or :data:`None`) is the pattern to compile into a regular
  47. expression.
  48. *include* (:class:`bool` or :data:`None`) must be :data:`None`
  49. unless *pattern* is a precompiled regular expression (:class:`re.RegexObject`)
  50. in which case it is whether matched files should be included
  51. (:data:`True`), excluded (:data:`False`), or is a null operation
  52. (:data:`None`).
  53. .. NOTE:: Subclasses do not need to support the *include*
  54. parameter.
  55. """
  56. self.regex = None
  57. """
  58. *regex* (:class:`re.RegexObject`) is the regular expression for the
  59. pattern.
  60. """
  61. if isinstance(pattern, (unicode, bytes)):
  62. assert include is None, "include:{!r} must be null when pattern:{!r} is a string.".format(include, pattern)
  63. regex, include = self.pattern_to_regex(pattern)
  64. # NOTE: Make sure to allow a null regular expression to be
  65. # returned for a null-operation.
  66. if include is not None:
  67. regex = re.compile(regex)
  68. elif pattern is not None and hasattr(pattern, 'match'):
  69. # Assume pattern is a precompiled regular expression.
  70. # - NOTE: Used specified *include*.
  71. regex = pattern
  72. elif pattern is None:
  73. # NOTE: Make sure to allow a null pattern to be passed for a
  74. # null-operation.
  75. assert include is None, "include:{!r} must be null when pattern:{!r} is null.".format(include, pattern)
  76. else:
  77. raise TypeError("pattern:{!r} is not a string, RegexObject, or None.".format(pattern))
  78. super(RegexPattern, self).__init__(include)
  79. self.regex = regex
  80. def __eq__(self, other):
  81. """
  82. Tests the equality of this regex pattern with *other* (:class:`RegexPattern`)
  83. by comparing their :attr:`~Pattern.include` and :attr:`~RegexPattern.regex`
  84. attributes.
  85. """
  86. if isinstance(other, RegexPattern):
  87. return self.include == other.include and self.regex == other.regex
  88. else:
  89. return NotImplemented
  90. def match(self, files):
  91. """
  92. Matches this pattern against the specified files.
  93. *files* (:class:`~collections.abc.Iterable` of :class:`str`)
  94. contains each file relative to the root directory (e.g., "relative/path/to/file").
  95. Returns an :class:`~collections.abc.Iterable` yielding each matched
  96. file path (:class:`str`).
  97. """
  98. if self.include is not None:
  99. for path in files:
  100. if self.regex.match(path) is not None:
  101. yield path
  102. @classmethod
  103. def pattern_to_regex(cls, pattern):
  104. """
  105. Convert the pattern into an uncompiled regular expression.
  106. *pattern* (:class:`str`) is the pattern to convert into a regular
  107. expression.
  108. Returns the uncompiled regular expression (:class:`str` or :data:`None`),
  109. and whether matched files should be included (:data:`True`),
  110. excluded (:data:`False`), or is a null-operation (:data:`None`).
  111. .. NOTE:: The default implementation simply returns *pattern* and
  112. :data:`True`.
  113. """
  114. return pattern, True