Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/prompt_toolkit/filters/base.py: 80%
115 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1from __future__ import annotations
3from abc import ABCMeta, abstractmethod
4from typing import Callable, Iterable, Union
6__all__ = ["Filter", "Never", "Always", "Condition", "FilterOrBool"]
9class Filter(metaclass=ABCMeta):
10 """
11 Base class for any filter to activate/deactivate a feature, depending on a
12 condition.
14 The return value of ``__call__`` will tell if the feature should be active.
15 """
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
22 @abstractmethod
23 def __call__(self) -> bool:
24 """
25 The actual call to evaluate the filter.
26 """
27 return True
29 def __and__(self, other: Filter) -> Filter:
30 """
31 Chaining of filters using the & operator.
32 """
33 assert isinstance(other, Filter), "Expecting filter, got %r" % other
35 if isinstance(other, Always):
36 return self
37 if isinstance(other, Never):
38 return other
40 if other in self._and_cache:
41 return self._and_cache[other]
43 result = _AndList.create([self, other])
44 self._and_cache[other] = result
45 return result
47 def __or__(self, other: Filter) -> Filter:
48 """
49 Chaining of filters using the | operator.
50 """
51 assert isinstance(other, Filter), "Expecting filter, got %r" % other
53 if isinstance(other, Always):
54 return other
55 if isinstance(other, Never):
56 return self
58 if other in self._or_cache:
59 return self._or_cache[other]
61 result = _OrList.create([self, other])
62 self._or_cache[other] = result
63 return result
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)
72 return self._invert_result
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.
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. "
85 "Instead, call it as a function."
86 )
89def _remove_duplicates(filters: list[Filter]) -> list[Filter]:
90 result = []
91 for f in filters:
92 if f not in result:
93 result.append(f)
94 return result
97class _AndList(Filter):
98 """
99 Result of &-operation between several filters.
100 """
102 def __init__(self, filters: list[Filter]) -> None:
103 super().__init__()
104 self.filters = filters
106 @classmethod
107 def create(cls, filters: Iterable[Filter]) -> Filter:
108 """
109 Create a new filter by applying an `&` operator between them.
111 If there's only one unique filter in the given iterable, it will return
112 that one filter instead of an `_AndList`.
113 """
114 filters_2: list[Filter] = []
116 for f in filters:
117 if isinstance(f, _AndList): # Turn nested _AndLists into one.
118 filters_2.extend(f.filters)
119 else:
120 filters_2.append(f)
122 # Remove duplicates. This could speed up execution, and doesn't make a
123 # difference for the evaluation.
124 filters = _remove_duplicates(filters_2)
126 # If only one filter is left, return that without wrapping into an
127 # `_AndList`.
128 if len(filters) == 1:
129 return filters[0]
131 return cls(filters)
133 def __call__(self) -> bool:
134 return all(f() for f in self.filters)
136 def __repr__(self) -> str:
137 return "&".join(repr(f) for f in self.filters)
140class _OrList(Filter):
141 """
142 Result of |-operation between several filters.
143 """
145 def __init__(self, filters: list[Filter]) -> None:
146 super().__init__()
147 self.filters = filters
149 @classmethod
150 def create(cls, filters: Iterable[Filter]) -> Filter:
151 """
152 Create a new filter by applying an `|` operator between them.
154 If there's only one unique filter in the given iterable, it will return
155 that one filter instead of an `_OrList`.
156 """
157 filters_2: list[Filter] = []
159 for f in filters:
160 if isinstance(f, _OrList): # Turn nested _AndLists into one.
161 filters_2.extend(f.filters)
162 else:
163 filters_2.append(f)
165 # Remove duplicates. This could speed up execution, and doesn't make a
166 # difference for the evaluation.
167 filters = _remove_duplicates(filters_2)
169 # If only one filter is left, return that without wrapping into an
170 # `_AndList`.
171 if len(filters) == 1:
172 return filters[0]
174 return cls(filters)
176 def __call__(self) -> bool:
177 return any(f() for f in self.filters)
179 def __repr__(self) -> str:
180 return "|".join(repr(f) for f in self.filters)
183class _Invert(Filter):
184 """
185 Negation of another filter.
186 """
188 def __init__(self, filter: Filter) -> None:
189 super().__init__()
190 self.filter = filter
192 def __call__(self) -> bool:
193 return not self.filter()
195 def __repr__(self) -> str:
196 return "~%r" % self.filter
199class Always(Filter):
200 """
201 Always enable feature.
202 """
204 def __call__(self) -> bool:
205 return True
207 def __or__(self, other: Filter) -> Filter:
208 return self
210 def __invert__(self) -> Never:
211 return Never()
214class Never(Filter):
215 """
216 Never enable feature.
217 """
219 def __call__(self) -> bool:
220 return False
222 def __and__(self, other: Filter) -> Filter:
223 return self
225 def __invert__(self) -> Always:
226 return Always()
229class Condition(Filter):
230 """
231 Turn any callable into a Filter. The callable is supposed to not take any
232 arguments.
234 This can be used as a decorator::
236 @Condition
237 def feature_is_active(): # `feature_is_active` becomes a Filter.
238 return True
240 :param func: Callable which takes no inputs and returns a boolean.
241 """
243 def __init__(self, func: Callable[[], bool]) -> None:
244 super().__init__()
245 self.func = func
247 def __call__(self) -> bool:
248 return self.func()
250 def __repr__(self) -> str:
251 return "Condition(%r)" % self.func
254# Often used as type annotation.
255FilterOrBool = Union[Filter, bool]