Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pathspec/pattern.py: 64%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

53 statements  

1""" 

2This module provides the base definition for patterns. 

3""" 

4 

5import dataclasses 

6import re 

7import warnings 

8from typing import ( 

9 Any, 

10 AnyStr, 

11 Iterable, # Replaced by `collections.abc.Iterable` in 3.9. 

12 Iterator, # Replaced by `collections.abc.Iterator` in 3.9. 

13 Match as MatchHint, # Replaced by `re.Match` in 3.9. 

14 Optional, # Replaced by `X | None` in 3.10. 

15 Pattern as PatternHint, # Replaced by `re.Pattern` in 3.9. 

16 Tuple, # Replaced by `tuple` in 3.9. 

17 Union) # Replaced by `X | Y` in 3.10. 

18 

19 

20class Pattern(object): 

21 """ 

22 The :class:`Pattern` class is the abstract definition of a pattern. 

23 """ 

24 

25 # Make the class dict-less. 

26 __slots__ = ( 

27 'include', 

28 ) 

29 

30 def __init__(self, include: Optional[bool]) -> None: 

31 """ 

32 Initializes the :class:`Pattern` instance. 

33 

34 *include* (:class:`bool` or :data:`None`) is whether the matched files 

35 should be included (:data:`True`), excluded (:data:`False`), or is a 

36 null-operation (:data:`None`). 

37 """ 

38 

39 self.include = include 

40 """ 

41 *include* (:class:`bool` or :data:`None`) is whether the matched files 

42 should be included (:data:`True`), excluded (:data:`False`), or is a 

43 null-operation (:data:`None`). 

44 """ 

45 

46 def match(self, files: Iterable[str]) -> Iterator[str]: 

47 """ 

48 DEPRECATED: This method is no longer used and has been replaced by 

49 :meth:`.match_file`. Use the :meth:`.match_file` method with a loop for 

50 similar results. 

51 

52 Matches this pattern against the specified files. 

53 

54 *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains each 

55 file relative to the root directory (e.g., ``"relative/path/to/file"``). 

56 

57 Returns an :class:`~collections.abc.Iterable` yielding each matched file 

58 path (:class:`str`). 

59 """ 

60 warnings.warn(( 

61 "{cls.__module__}.{cls.__qualname__}.match() is deprecated. Use " 

62 "{cls.__module__}.{cls.__qualname__}.match_file() with a loop for " 

63 "similar results." 

64 ).format(cls=self.__class__), DeprecationWarning, stacklevel=2) 

65 

66 for file in files: 

67 if self.match_file(file) is not None: 

68 yield file 

69 

70 def match_file(self, file: str) -> Optional[Any]: 

71 """ 

72 Matches this pattern against the specified file. 

73 

74 *file* (:class:`str`) is the normalized file path to match against. 

75 

76 Returns the match result if *file* matched; otherwise, :data:`None`. 

77 """ 

78 raise NotImplementedError(( 

79 "{cls.__module__}.{cls.__qualname__} must override match_file()." 

80 ).format(cls=self.__class__)) 

81 

82 

83class RegexPattern(Pattern): 

84 """ 

85 The :class:`RegexPattern` class is an implementation of a pattern using 

86 regular expressions. 

87 """ 

88 

89 # Keep the class dict-less. 

90 __slots__ = ( 

91 'pattern', 

92 'regex', 

93 ) 

94 

95 def __init__( 

96 self, 

97 pattern: Union[AnyStr, PatternHint, None], 

98 include: Optional[bool] = None, 

99 ) -> None: 

100 """ 

101 Initializes the :class:`RegexPattern` instance. 

102 

103 *pattern* (:class:`str`, :class:`bytes`, :class:`re.Pattern`, or 

104 :data:`None`) is the pattern to compile into a regular expression. 

105 

106 *include* (:class:`bool` or :data:`None`) must be :data:`None` unless 

107 *pattern* is a precompiled regular expression (:class:`re.Pattern`) in which 

108 case it is whether matched files should be included (:data:`True`), excluded 

109 (:data:`False`), or is a null operation (:data:`None`). 

110 

111 .. NOTE:: Subclasses do not need to support the *include* parameter. 

112 """ 

113 

114 if isinstance(pattern, (str, bytes)): 

115 assert include is None, ( 

116 f"include:{include!r} must be null when pattern:{pattern!r} is a string." 

117 ) 

118 regex, include = self.pattern_to_regex(pattern) 

119 # NOTE: Make sure to allow a null regular expression to be 

120 # returned for a null-operation. 

121 if include is not None: 

122 regex = re.compile(regex) 

123 

124 elif pattern is not None and hasattr(pattern, 'match'): 

125 # Assume pattern is a precompiled regular expression. 

126 # - NOTE: Used specified *include*. 

127 regex = pattern 

128 

129 elif pattern is None: 

130 # NOTE: Make sure to allow a null pattern to be passed for a 

131 # null-operation. 

132 assert include is None, ( 

133 f"include:{include!r} must be null when pattern:{pattern!r} is null." 

134 ) 

135 

136 else: 

137 raise TypeError(f"pattern:{pattern!r} is not a string, re.Pattern, or None.") 

138 

139 super(RegexPattern, self).__init__(include) 

140 

141 self.pattern: Union[AnyStr, PatternHint, None] = pattern 

142 """ 

143 *pattern* (:class:`str`, :class:`bytes`, :class:`re.Pattern`, or 

144 :data:`None`) is the uncompiled, input pattern. This is for reference. 

145 """ 

146 

147 self.regex: PatternHint = regex 

148 """ 

149 *regex* (:class:`re.Pattern`) is the regular expression for the pattern. 

150 """ 

151 

152 def __eq__(self, other: 'RegexPattern') -> bool: 

153 """ 

154 Tests the equality of this regex pattern with *other* (:class:`RegexPattern`) 

155 by comparing their :attr:`~Pattern.include` and :attr:`~RegexPattern.regex` 

156 attributes. 

157 """ 

158 if isinstance(other, RegexPattern): 

159 return self.include == other.include and self.regex == other.regex 

160 else: 

161 return NotImplemented 

162 

163 def match_file(self, file: str) -> Optional['RegexMatchResult']: 

164 """ 

165 Matches this pattern against the specified file. 

166 

167 *file* (:class:`str`) contains each file relative to the root directory 

168 (e.g., "relative/path/to/file"). 

169 

170 Returns the match result (:class:`.RegexMatchResult`) if *file* matched; 

171 otherwise, :data:`None`. 

172 """ 

173 if self.include is not None: 

174 match = self.regex.match(file) 

175 if match is not None: 

176 return RegexMatchResult(match) 

177 

178 return None 

179 

180 @classmethod 

181 def pattern_to_regex(cls, pattern: str) -> Tuple[str, bool]: 

182 """ 

183 Convert the pattern into an uncompiled regular expression. 

184 

185 *pattern* (:class:`str`) is the pattern to convert into a regular 

186 expression. 

187 

188 Returns the uncompiled regular expression (:class:`str` or :data:`None`), 

189 and whether matched files should be included (:data:`True`), excluded 

190 (:data:`False`), or is a null-operation (:data:`None`). 

191 

192 .. NOTE:: The default implementation simply returns *pattern* and 

193 :data:`True`. 

194 """ 

195 return pattern, True 

196 

197 

198@dataclasses.dataclass() 

199class RegexMatchResult(object): 

200 """ 

201 The :class:`RegexMatchResult` data class is used to return information about 

202 the matched regular expression. 

203 """ 

204 

205 # Keep the class dict-less. 

206 __slots__ = ( 

207 'match', 

208 ) 

209 

210 match: MatchHint 

211 """ 

212 *match* (:class:`re.Match`) is the regex match result. 

213 """