Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/prompt_toolkit/filters/base.py: 79%

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

119 statements  

1from __future__ import annotations 

2 

3from abc import ABCMeta, abstractmethod 

4from typing import Callable, Iterable, Union 

5 

6__all__ = ["Filter", "Never", "Always", "Condition", "FilterOrBool"] 

7 

8 

9class Filter(metaclass=ABCMeta): 

10 """ 

11 Base class for any filter to activate/deactivate a feature, depending on a 

12 condition. 

13 

14 The return value of ``__call__`` will tell if the feature should be active. 

15 """ 

16 

17 def __init__(self) -> None: 

18 self._and_cache: dict[Filter, Filter] = {} 

19 self._or_cache: dict[Filter, Filter] = {} 

20 self._invert_result: Filter | None = None 

21 

22 @abstractmethod 

23 def __call__(self) -> bool: 

24 """ 

25 The actual call to evaluate the filter. 

26 """ 

27 return True 

28 

29 def __and__(self, other: Filter) -> Filter: 

30 """ 

31 Chaining of filters using the & operator. 

32 """ 

33 assert isinstance(other, Filter), f"Expecting filter, got {other!r}" 

34 

35 if isinstance(other, Always): 

36 return self 

37 if isinstance(other, Never): 

38 return other 

39 

40 if other in self._and_cache: 

41 return self._and_cache[other] 

42 

43 result = _AndList.create([self, other]) 

44 self._and_cache[other] = result 

45 return result 

46 

47 def __or__(self, other: Filter) -> Filter: 

48 """ 

49 Chaining of filters using the | operator. 

50 """ 

51 assert isinstance(other, Filter), f"Expecting filter, got {other!r}" 

52 

53 if isinstance(other, Always): 

54 return other 

55 if isinstance(other, Never): 

56 return self 

57 

58 if other in self._or_cache: 

59 return self._or_cache[other] 

60 

61 result = _OrList.create([self, other]) 

62 self._or_cache[other] = result 

63 return result 

64 

65 def __invert__(self) -> Filter: 

66 """ 

67 Inverting of filters using the ~ operator. 

68 """ 

69 if self._invert_result is None: 

70 self._invert_result = _Invert(self) 

71 

72 return self._invert_result 

73 

74 def __bool__(self) -> None: 

75 """ 

76 By purpose, we don't allow bool(...) operations directly on a filter, 

77 because the meaning is ambiguous. 

78 

79 Executing a filter has to be done always by calling it. Providing 

80 defaults for `None` values should be done through an `is None` check 

81 instead of for instance ``filter1 or Always()``. 

82 """ 

83 raise ValueError( 

84 "The truth value of a Filter is ambiguous. Instead, call it as a function." 

85 ) 

86 

87 

88def _remove_duplicates(filters: list[Filter]) -> list[Filter]: 

89 result = [] 

90 for f in filters: 

91 if f not in result: 

92 result.append(f) 

93 return result 

94 

95 

96class _AndList(Filter): 

97 """ 

98 Result of &-operation between several filters. 

99 """ 

100 

101 def __init__(self, filters: list[Filter]) -> None: 

102 super().__init__() 

103 self.filters = filters 

104 

105 @classmethod 

106 def create(cls, filters: Iterable[Filter]) -> Filter: 

107 """ 

108 Create a new filter by applying an `&` operator between them. 

109 

110 If there's only one unique filter in the given iterable, it will return 

111 that one filter instead of an `_AndList`. 

112 """ 

113 filters_2: list[Filter] = [] 

114 

115 for f in filters: 

116 if isinstance(f, _AndList): # Turn nested _AndLists into one. 

117 filters_2.extend(f.filters) 

118 else: 

119 filters_2.append(f) 

120 

121 # Remove duplicates. This could speed up execution, and doesn't make a 

122 # difference for the evaluation. 

123 filters = _remove_duplicates(filters_2) 

124 

125 # If only one filter is left, return that without wrapping into an 

126 # `_AndList`. 

127 if len(filters) == 1: 

128 return filters[0] 

129 

130 return cls(filters) 

131 

132 def __call__(self) -> bool: 

133 return all(f() for f in self.filters) 

134 

135 def __repr__(self) -> str: 

136 return "&".join(repr(f) for f in self.filters) 

137 

138 

139class _OrList(Filter): 

140 """ 

141 Result of |-operation between several filters. 

142 """ 

143 

144 def __init__(self, filters: list[Filter]) -> None: 

145 super().__init__() 

146 self.filters = filters 

147 

148 @classmethod 

149 def create(cls, filters: Iterable[Filter]) -> Filter: 

150 """ 

151 Create a new filter by applying an `|` operator between them. 

152 

153 If there's only one unique filter in the given iterable, it will return 

154 that one filter instead of an `_OrList`. 

155 """ 

156 filters_2: list[Filter] = [] 

157 

158 for f in filters: 

159 if isinstance(f, _OrList): # Turn nested _AndLists into one. 

160 filters_2.extend(f.filters) 

161 else: 

162 filters_2.append(f) 

163 

164 # Remove duplicates. This could speed up execution, and doesn't make a 

165 # difference for the evaluation. 

166 filters = _remove_duplicates(filters_2) 

167 

168 # If only one filter is left, return that without wrapping into an 

169 # `_AndList`. 

170 if len(filters) == 1: 

171 return filters[0] 

172 

173 return cls(filters) 

174 

175 def __call__(self) -> bool: 

176 return any(f() for f in self.filters) 

177 

178 def __repr__(self) -> str: 

179 return "|".join(repr(f) for f in self.filters) 

180 

181 

182class _Invert(Filter): 

183 """ 

184 Negation of another filter. 

185 """ 

186 

187 def __init__(self, filter: Filter) -> None: 

188 super().__init__() 

189 self.filter = filter 

190 

191 def __call__(self) -> bool: 

192 return not self.filter() 

193 

194 def __repr__(self) -> str: 

195 return f"~{self.filter!r}" 

196 

197 

198class Always(Filter): 

199 """ 

200 Always enable feature. 

201 """ 

202 

203 def __call__(self) -> bool: 

204 return True 

205 

206 def __or__(self, other: Filter) -> Filter: 

207 return self 

208 

209 def __and__(self, other: Filter) -> Filter: 

210 return other 

211 

212 def __invert__(self) -> Never: 

213 return Never() 

214 

215 

216class Never(Filter): 

217 """ 

218 Never enable feature. 

219 """ 

220 

221 def __call__(self) -> bool: 

222 return False 

223 

224 def __and__(self, other: Filter) -> Filter: 

225 return self 

226 

227 def __or__(self, other: Filter) -> Filter: 

228 return other 

229 

230 def __invert__(self) -> Always: 

231 return Always() 

232 

233 

234class Condition(Filter): 

235 """ 

236 Turn any callable into a Filter. The callable is supposed to not take any 

237 arguments. 

238 

239 This can be used as a decorator:: 

240 

241 @Condition 

242 def feature_is_active(): # `feature_is_active` becomes a Filter. 

243 return True 

244 

245 :param func: Callable which takes no inputs and returns a boolean. 

246 """ 

247 

248 def __init__(self, func: Callable[[], bool]) -> None: 

249 super().__init__() 

250 self.func = func 

251 

252 def __call__(self) -> bool: 

253 return self.func() 

254 

255 def __repr__(self) -> str: 

256 return f"Condition({self.func!r})" 

257 

258 

259# Often used as type annotation. 

260FilterOrBool = Union[Filter, bool]