1
1
from abc import ABCMeta , abstractmethod
2
- from typing import Callable , Dict , Iterable , List , Tuple , Union
2
+ from typing import Callable , Dict , Iterable , List , Optional , Tuple , Union
3
3
4
4
__all__ = ["Filter" , "Never" , "Always" , "Condition" , "FilterOrBool" ]
5
5
@@ -12,6 +12,11 @@ class Filter(metaclass=ABCMeta):
12
12
The return value of ``__call__`` will tell if the feature should be active.
13
13
"""
14
14
15
+ def __init__ (self ) -> None :
16
+ self ._and_cache : Dict [Filter , _AndList ] = {}
17
+ self ._or_cache : Dict [Filter , _AndList ] = {}
18
+ self ._invert_result : Optional [Filter ] = None
19
+
15
20
@abstractmethod
16
21
def __call__ (self ) -> bool :
17
22
"""
@@ -23,19 +28,40 @@ def __and__(self, other: "Filter") -> "Filter":
23
28
"""
24
29
Chaining of filters using the & operator.
25
30
"""
26
- return _and_cache [self , other ]
31
+ assert isinstance (other , Filter ), "Expecting filter, got %r" % other
32
+
33
+ if isinstance (other , Always ):
34
+ return self
35
+ if isinstance (other , Never ):
36
+ return other
37
+
38
+ result = _AndList ([self , other ])
39
+ self ._and_cache [other ] = result
40
+ return result
27
41
28
42
def __or__ (self , other : "Filter" ) -> "Filter" :
29
43
"""
30
44
Chaining of filters using the | operator.
31
45
"""
32
- return _or_cache [self , other ]
46
+ assert isinstance (other , Filter ), "Expecting filter, got %r" % other
47
+
48
+ if isinstance (other , Always ):
49
+ return other
50
+ if isinstance (other , Never ):
51
+ return self
52
+
53
+ result = _OrList ([self , other ])
54
+ self ._or_cache [other ] = result
55
+ return result
33
56
34
57
def __invert__ (self ) -> "Filter" :
35
58
"""
36
59
Inverting of filters using the ~ operator.
37
60
"""
38
- return _invert_cache [self ]
61
+ if self ._invert_result is None :
62
+ self ._invert_result = _Invert (self )
63
+
64
+ return self ._invert_result
39
65
40
66
def __bool__ (self ) -> None :
41
67
"""
@@ -52,68 +78,13 @@ def __bool__(self) -> None:
52
78
)
53
79
54
80
55
- class _AndCache (Dict [Tuple [Filter , Filter ], "_AndList" ]):
56
- """
57
- Cache for And operation between filters.
58
- (Filter classes are stateless, so we can reuse them.)
59
-
60
- Note: This could be a memory leak if we keep creating filters at runtime.
61
- If that is True, the filters should be weakreffed (not the tuple of
62
- filters), and tuples should be removed when one of these filters is
63
- removed. In practise however, there is a finite amount of filters.
64
- """
65
-
66
- def __missing__ (self , filters : Tuple [Filter , Filter ]) -> Filter :
67
- a , b = filters
68
- assert isinstance (b , Filter ), "Expecting filter, got %r" % b
69
-
70
- if isinstance (b , Always ) or isinstance (a , Never ):
71
- return a
72
- elif isinstance (b , Never ) or isinstance (a , Always ):
73
- return b
74
-
75
- result = _AndList (filters )
76
- self [filters ] = result
77
- return result
78
-
79
-
80
- class _OrCache (Dict [Tuple [Filter , Filter ], "_OrList" ]):
81
- """Cache for Or operation between filters."""
82
-
83
- def __missing__ (self , filters : Tuple [Filter , Filter ]) -> Filter :
84
- a , b = filters
85
- assert isinstance (b , Filter ), "Expecting filter, got %r" % b
86
-
87
- if isinstance (b , Always ) or isinstance (a , Never ):
88
- return b
89
- elif isinstance (b , Never ) or isinstance (a , Always ):
90
- return a
91
-
92
- result = _OrList (filters )
93
- self [filters ] = result
94
- return result
95
-
96
-
97
- class _InvertCache (Dict [Filter , "_Invert" ]):
98
- """Cache for inversion operator."""
99
-
100
- def __missing__ (self , filter : Filter ) -> Filter :
101
- result = _Invert (filter )
102
- self [filter ] = result
103
- return result
104
-
105
-
106
- _and_cache = _AndCache ()
107
- _or_cache = _OrCache ()
108
- _invert_cache = _InvertCache ()
109
-
110
-
111
81
class _AndList (Filter ):
112
82
"""
113
83
Result of &-operation between several filters.
114
84
"""
115
85
116
86
def __init__ (self , filters : Iterable [Filter ]) -> None :
87
+ super ().__init__ ()
117
88
self .filters : List [Filter ] = []
118
89
119
90
for f in filters :
@@ -135,6 +106,7 @@ class _OrList(Filter):
135
106
"""
136
107
137
108
def __init__ (self , filters : Iterable [Filter ]) -> None :
109
+ super ().__init__ ()
138
110
self .filters : List [Filter ] = []
139
111
140
112
for f in filters :
@@ -156,6 +128,7 @@ class _Invert(Filter):
156
128
"""
157
129
158
130
def __init__ (self , filter : Filter ) -> None :
131
+ super ().__init__ ()
159
132
self .filter = filter
160
133
161
134
def __call__ (self ) -> bool :
@@ -173,6 +146,9 @@ class Always(Filter):
173
146
def __call__ (self ) -> bool :
174
147
return True
175
148
149
+ def __or__ (self , other : "Filter" ) -> "Filter" :
150
+ return self
151
+
176
152
def __invert__ (self ) -> "Never" :
177
153
return Never ()
178
154
@@ -185,6 +161,9 @@ class Never(Filter):
185
161
def __call__ (self ) -> bool :
186
162
return False
187
163
164
+ def __and__ (self , other : "Filter" ) -> "Filter" :
165
+ return self
166
+
188
167
def __invert__ (self ) -> Always :
189
168
return Always ()
190
169
@@ -204,6 +183,7 @@ def feature_is_active(): # `feature_is_active` becomes a Filter.
204
183
"""
205
184
206
185
def __init__ (self , func : Callable [[], bool ]) -> None :
186
+ super ().__init__ ()
207
187
self .func = func
208
188
209
189
def __call__ (self ) -> bool :
0 commit comments