1
+ import weakref
1
2
from abc import ABCMeta , abstractmethod
2
- from typing import Callable , Dict , Iterable , List , Tuple , Union
3
+ from typing import Callable , Dict , Iterable , List , Optional , Tuple , Union
3
4
4
5
__all__ = ["Filter" , "Never" , "Always" , "Condition" , "FilterOrBool" ]
5
6
@@ -12,6 +13,15 @@ class Filter(metaclass=ABCMeta):
12
13
The return value of ``__call__`` will tell if the feature should be active.
13
14
"""
14
15
16
+ def __init__ (self ) -> None :
17
+ self ._and_cache : "weakref.WeakValueDictionary[Filter, _AndList]" = (
18
+ weakref .WeakValueDictionary ()
19
+ )
20
+ self ._or_cache : "weakref.WeakValueDictionary[Filter, _OrList]" = (
21
+ weakref .WeakValueDictionary ()
22
+ )
23
+ self ._invert_result : Optional [Filter ] = None
24
+
15
25
@abstractmethod
16
26
def __call__ (self ) -> bool :
17
27
"""
@@ -23,19 +33,46 @@ def __and__(self, other: "Filter") -> "Filter":
23
33
"""
24
34
Chaining of filters using the & operator.
25
35
"""
26
- return _and_cache [self , other ]
36
+ assert isinstance (other , Filter ), "Expecting filter, got %r" % other
37
+
38
+ if isinstance (other , Always ):
39
+ return self
40
+ if isinstance (other , Never ):
41
+ return other
42
+
43
+ if other in self ._and_cache :
44
+ return self ._and_cache [other ]
45
+
46
+ result = _AndList ([self , other ])
47
+ self ._and_cache [other ] = result
48
+ return result
27
49
28
50
def __or__ (self , other : "Filter" ) -> "Filter" :
29
51
"""
30
52
Chaining of filters using the | operator.
31
53
"""
32
- return _or_cache [self , other ]
54
+ assert isinstance (other , Filter ), "Expecting filter, got %r" % other
55
+
56
+ if isinstance (other , Always ):
57
+ return other
58
+ if isinstance (other , Never ):
59
+ return self
60
+
61
+ if other in self ._or_cache :
62
+ return self ._or_cache [other ]
63
+
64
+ result = _OrList ([self , other ])
65
+ self ._or_cache [other ] = result
66
+ return result
33
67
34
68
def __invert__ (self ) -> "Filter" :
35
69
"""
36
70
Inverting of filters using the ~ operator.
37
71
"""
38
- return _invert_cache [self ]
72
+ if self ._invert_result is None :
73
+ self ._invert_result = _Invert (self )
74
+
75
+ return self ._invert_result
39
76
40
77
def __bool__ (self ) -> None :
41
78
"""
@@ -52,68 +89,13 @@ def __bool__(self) -> None:
52
89
)
53
90
54
91
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
92
class _AndList (Filter ):
112
93
"""
113
94
Result of &-operation between several filters.
114
95
"""
115
96
116
97
def __init__ (self , filters : Iterable [Filter ]) -> None :
98
+ super ().__init__ ()
117
99
self .filters : List [Filter ] = []
118
100
119
101
for f in filters :
@@ -135,6 +117,7 @@ class _OrList(Filter):
135
117
"""
136
118
137
119
def __init__ (self , filters : Iterable [Filter ]) -> None :
120
+ super ().__init__ ()
138
121
self .filters : List [Filter ] = []
139
122
140
123
for f in filters :
@@ -156,6 +139,7 @@ class _Invert(Filter):
156
139
"""
157
140
158
141
def __init__ (self , filter : Filter ) -> None :
142
+ super ().__init__ ()
159
143
self .filter = filter
160
144
161
145
def __call__ (self ) -> bool :
@@ -173,6 +157,9 @@ class Always(Filter):
173
157
def __call__ (self ) -> bool :
174
158
return True
175
159
160
+ def __or__ (self , other : "Filter" ) -> "Filter" :
161
+ return self
162
+
176
163
def __invert__ (self ) -> "Never" :
177
164
return Never ()
178
165
@@ -185,6 +172,9 @@ class Never(Filter):
185
172
def __call__ (self ) -> bool :
186
173
return False
187
174
175
+ def __and__ (self , other : "Filter" ) -> "Filter" :
176
+ return self
177
+
188
178
def __invert__ (self ) -> Always :
189
179
return Always ()
190
180
@@ -204,6 +194,7 @@ def feature_is_active(): # `feature_is_active` becomes a Filter.
204
194
"""
205
195
206
196
def __init__ (self , func : Callable [[], bool ]) -> None :
197
+ super ().__init__ ()
207
198
self .func = func
208
199
209
200
def __call__ (self ) -> bool :
0 commit comments