py-predicate 0.4__tar.gz → 0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {py_predicate-0.4 → py_predicate-0.6}/PKG-INFO +3 -3
- {py_predicate-0.4 → py_predicate-0.6}/README.md +2 -2
- {py_predicate-0.4 → py_predicate-0.6}/predicate/__init__.py +65 -25
- py_predicate-0.6/predicate/all_predicate.py +25 -0
- py_predicate-0.6/predicate/any_predicate.py +21 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/comp_predicate.py +5 -1
- py_predicate-0.6/predicate/dict_of_predicate.py +51 -0
- py_predicate-0.6/predicate/eq_predicate.py +21 -0
- py_predicate-0.6/predicate/explain.py +7 -0
- py_predicate-0.6/predicate/fn_predicate.py +21 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/formatter/format_dot.py +78 -57
- {py_predicate-0.4 → py_predicate-0.6}/predicate/formatter/format_json.py +2 -2
- py_predicate-0.6/predicate/ge_predicate.py +21 -0
- py_predicate-0.6/predicate/generator/generate_false.py +351 -0
- py_predicate-0.6/predicate/generator/generate_true.py +411 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/generator/helpers.py +45 -11
- py_predicate-0.6/predicate/gt_predicate.py +21 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/has_key_predicate.py +6 -5
- py_predicate-0.6/predicate/has_length_predicate.py +23 -0
- py_predicate-0.6/predicate/implies.py +109 -0
- py_predicate-0.6/predicate/ip_address_predicates.py +47 -0
- py_predicate-0.6/predicate/is_empty_predicate.py +41 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/is_instance_predicate.py +5 -3
- py_predicate-0.6/predicate/is_none_predicate.py +19 -0
- py_predicate-0.6/predicate/is_not_none_predicate.py +19 -0
- py_predicate-0.6/predicate/le_predicate.py +21 -0
- py_predicate-0.6/predicate/list_of_predicate.py +35 -0
- py_predicate-0.6/predicate/lt_predicate.py +21 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/named_predicate.py +0 -3
- py_predicate-0.6/predicate/ne_predicate.py +21 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/negate.py +2 -1
- {py_predicate-0.4 → py_predicate-0.6}/predicate/optimizer/all_optimizer.py +3 -3
- {py_predicate-0.4 → py_predicate-0.6}/predicate/optimizer/and_optimizer.py +13 -22
- {py_predicate-0.4 → py_predicate-0.6}/predicate/optimizer/any_optimizer.py +2 -2
- py_predicate-0.6/predicate/optimizer/in_optimizer.py +26 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/optimizer/or_optimizer.py +13 -10
- {py_predicate-0.4 → py_predicate-0.6}/predicate/optimizer/predicate_optimizer.py +7 -35
- {py_predicate-0.4 → py_predicate-0.6}/predicate/optimizer/xor_optimizer.py +7 -2
- py_predicate-0.6/predicate/predicate.py +276 -0
- py_predicate-0.6/predicate/property_predicate.py +28 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/range_predicate.py +20 -12
- py_predicate-0.6/predicate/set_of_predicate.py +25 -0
- py_predicate-0.6/predicate/set_predicates.py +120 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/standard_predicates.py +32 -35
- py_predicate-0.6/predicate/str_predicates.py +56 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/this_predicate.py +3 -0
- py_predicate-0.6/predicate/tuple_of_predicate.py +32 -0
- {py_predicate-0.4 → py_predicate-0.6}/pyproject.toml +5 -2
- py_predicate-0.4/predicate/all_predicate.py +0 -20
- py_predicate-0.4/predicate/any_predicate.py +0 -20
- py_predicate-0.4/predicate/generator/generate_false.py +0 -124
- py_predicate-0.4/predicate/generator/generate_true.py +0 -241
- py_predicate-0.4/predicate/implies.py +0 -64
- py_predicate-0.4/predicate/optimizer/in_optimizer.py +0 -13
- py_predicate-0.4/predicate/optimizer/rules.py +0 -71
- py_predicate-0.4/predicate/predicate.py +0 -427
- {py_predicate-0.4 → py_predicate-0.6}/predicate/constructor/__init__.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/constructor/construct.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/formatter/__init__.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/generator/__init__.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/lazy_predicate.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/optimizer/__init__.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/optimizer/not_optimizer.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/parser.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/regex_predicate.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/root_predicate.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/tee_predicate.py +0 -0
- {py_predicate-0.4 → py_predicate-0.6}/predicate/truth_table.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: py_predicate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6
|
|
4
4
|
Summary: Module to create composable predicates
|
|
5
5
|
Author-email: Maurits Rijk <maurits.rijk@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -91,9 +91,9 @@ either a string, or a list of data that can again either be a string or a list o
|
|
|
91
91
|
data. Ad infinitum.
|
|
92
92
|
|
|
93
93
|
```python
|
|
94
|
-
from predicate import all_p, is_list_p, is_str_p,
|
|
94
|
+
from predicate import all_p, is_list_p, is_str_p, root_p
|
|
95
95
|
|
|
96
|
-
str_or_list_of_str = is_str_p | (is_list_p & all_p(
|
|
96
|
+
str_or_list_of_str = is_str_p | (is_list_p & all_p(root_p))
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
Using plain Python, the above one-liner would have to be coded as a (recursive) function.
|
|
@@ -48,9 +48,9 @@ either a string, or a list of data that can again either be a string or a list o
|
|
|
48
48
|
data. Ad infinitum.
|
|
49
49
|
|
|
50
50
|
```python
|
|
51
|
-
from predicate import all_p, is_list_p, is_str_p,
|
|
51
|
+
from predicate import all_p, is_list_p, is_str_p, root_p
|
|
52
52
|
|
|
53
|
-
str_or_list_of_str = is_str_p | (is_list_p & all_p(
|
|
53
|
+
str_or_list_of_str = is_str_p | (is_list_p & all_p(root_p))
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
Using plain Python, the above one-liner would have to be coded as a (recursive) function.
|
|
@@ -4,34 +4,41 @@ __version__ = "0.0.1"
|
|
|
4
4
|
|
|
5
5
|
from predicate.all_predicate import AllPredicate
|
|
6
6
|
from predicate.any_predicate import AnyPredicate
|
|
7
|
+
from predicate.eq_predicate import EqPredicate
|
|
8
|
+
from predicate.explain import explain
|
|
7
9
|
from predicate.formatter.format_dot import to_dot
|
|
8
10
|
from predicate.formatter.format_json import to_json
|
|
11
|
+
from predicate.ge_predicate import GePredicate
|
|
9
12
|
from predicate.generator.generate_false import generate_false
|
|
10
13
|
from predicate.generator.generate_true import generate_true
|
|
14
|
+
from predicate.gt_predicate import GtPredicate
|
|
15
|
+
from predicate.is_empty_predicate import IsEmptyPredicate, IsNotEmptyPredicate, is_empty_p, is_not_empty_p
|
|
16
|
+
from predicate.is_none_predicate import IsNonePredicate
|
|
17
|
+
from predicate.is_not_none_predicate import IsNotNonePredicate
|
|
18
|
+
from predicate.le_predicate import LePredicate
|
|
19
|
+
from predicate.lt_predicate import LtPredicate
|
|
20
|
+
from predicate.ne_predicate import NePredicate
|
|
11
21
|
from predicate.optimizer.predicate_optimizer import can_optimize, optimize
|
|
12
22
|
from predicate.predicate import (
|
|
13
23
|
AlwaysFalsePredicate,
|
|
14
24
|
AlwaysTruePredicate,
|
|
15
25
|
AndPredicate,
|
|
16
|
-
EqPredicate,
|
|
17
|
-
FnPredicate,
|
|
18
|
-
GePredicate,
|
|
19
|
-
GtPredicate,
|
|
20
|
-
InPredicate,
|
|
21
|
-
IsEmptyPredicate,
|
|
22
|
-
IsNonePredicate,
|
|
23
|
-
IsNotNonePredicate,
|
|
24
|
-
LePredicate,
|
|
25
|
-
LtPredicate,
|
|
26
|
-
NePredicate,
|
|
27
|
-
NotInPredicate,
|
|
28
26
|
NotPredicate,
|
|
29
27
|
OrPredicate,
|
|
30
28
|
Predicate,
|
|
31
29
|
XorPredicate,
|
|
32
30
|
always_false_p,
|
|
33
31
|
always_true_p,
|
|
34
|
-
|
|
32
|
+
)
|
|
33
|
+
from predicate.set_predicates import (
|
|
34
|
+
InPredicate,
|
|
35
|
+
NotInPredicate,
|
|
36
|
+
in_p,
|
|
37
|
+
is_real_subset_p,
|
|
38
|
+
is_real_superset_p,
|
|
39
|
+
is_subset_p,
|
|
40
|
+
is_superset_p,
|
|
41
|
+
not_in_p,
|
|
35
42
|
)
|
|
36
43
|
from predicate.standard_predicates import (
|
|
37
44
|
all_p,
|
|
@@ -47,12 +54,14 @@ from predicate.standard_predicates import (
|
|
|
47
54
|
gt_le_p,
|
|
48
55
|
gt_lt_p,
|
|
49
56
|
gt_p,
|
|
50
|
-
|
|
57
|
+
has_key_p,
|
|
58
|
+
has_length_p,
|
|
51
59
|
is_bool_p,
|
|
52
60
|
is_callable_p,
|
|
53
61
|
is_complex_p,
|
|
54
62
|
is_container_p,
|
|
55
63
|
is_datetime_p,
|
|
64
|
+
is_dict_of_p,
|
|
56
65
|
is_dict_p,
|
|
57
66
|
is_falsy_p,
|
|
58
67
|
is_finite_p,
|
|
@@ -65,9 +74,11 @@ from predicate.standard_predicates import (
|
|
|
65
74
|
is_iterable_p,
|
|
66
75
|
is_list_of_p,
|
|
67
76
|
is_list_p,
|
|
77
|
+
is_nan_p,
|
|
68
78
|
is_none_p,
|
|
69
79
|
is_not_none_p,
|
|
70
80
|
is_predicate_p,
|
|
81
|
+
is_range_p,
|
|
71
82
|
is_set_of_p,
|
|
72
83
|
is_set_p,
|
|
73
84
|
is_str_p,
|
|
@@ -80,7 +91,6 @@ from predicate.standard_predicates import (
|
|
|
80
91
|
lt_p,
|
|
81
92
|
ne_p,
|
|
82
93
|
neg_p,
|
|
83
|
-
not_in_p,
|
|
84
94
|
pos_p,
|
|
85
95
|
regex_p,
|
|
86
96
|
root_p,
|
|
@@ -90,21 +100,21 @@ from predicate.standard_predicates import (
|
|
|
90
100
|
)
|
|
91
101
|
|
|
92
102
|
__all__ = [
|
|
103
|
+
"AllPredicate",
|
|
93
104
|
"AlwaysFalsePredicate",
|
|
94
105
|
"AlwaysTruePredicate",
|
|
95
|
-
"AllPredicate",
|
|
96
106
|
"AndPredicate",
|
|
97
107
|
"AnyPredicate",
|
|
98
108
|
"EqPredicate",
|
|
99
|
-
"FnPredicate",
|
|
100
109
|
"GePredicate",
|
|
101
110
|
"GtPredicate",
|
|
111
|
+
"LePredicate",
|
|
112
|
+
"LtPredicate",
|
|
102
113
|
"InPredicate",
|
|
103
114
|
"IsEmptyPredicate",
|
|
104
115
|
"IsNonePredicate",
|
|
116
|
+
"IsNotEmptyPredicate",
|
|
105
117
|
"IsNotNonePredicate",
|
|
106
|
-
"LePredicate",
|
|
107
|
-
"LtPredicate",
|
|
108
118
|
"NePredicate",
|
|
109
119
|
"NotInPredicate",
|
|
110
120
|
"NotPredicate",
|
|
@@ -120,7 +130,7 @@ __all__ = [
|
|
|
120
130
|
"eq_false_p",
|
|
121
131
|
"eq_p",
|
|
122
132
|
"eq_true_p",
|
|
123
|
-
"
|
|
133
|
+
"explain",
|
|
124
134
|
"fn_p",
|
|
125
135
|
"ge_le_p",
|
|
126
136
|
"ge_lt_p",
|
|
@@ -130,33 +140,52 @@ __all__ = [
|
|
|
130
140
|
"gt_le_p",
|
|
131
141
|
"gt_lt_p",
|
|
132
142
|
"gt_p",
|
|
143
|
+
"has_key_p",
|
|
144
|
+
"has_length_p",
|
|
133
145
|
"in_p",
|
|
146
|
+
"is_alnum_p",
|
|
147
|
+
"is_alpha_p",
|
|
148
|
+
"is_ascii_p",
|
|
134
149
|
"is_bool_p",
|
|
135
150
|
"is_callable_p",
|
|
136
151
|
"is_complex_p",
|
|
137
152
|
"is_container_p",
|
|
138
153
|
"is_datetime_p",
|
|
154
|
+
"is_decimal_p",
|
|
155
|
+
"is_dict_of_p",
|
|
139
156
|
"is_dict_p",
|
|
140
157
|
"is_empty_p",
|
|
158
|
+
"is_falsy_p",
|
|
141
159
|
"is_finite_p",
|
|
142
160
|
"is_float_p",
|
|
143
161
|
"is_hashable_p",
|
|
144
|
-
"
|
|
162
|
+
"is_identifier_p",
|
|
145
163
|
"is_inf_p",
|
|
164
|
+
"is_instance_p",
|
|
146
165
|
"is_int_p",
|
|
147
|
-
"is_iterable_p",
|
|
148
166
|
"is_iterable_of_p",
|
|
149
|
-
"
|
|
167
|
+
"is_iterable_p",
|
|
150
168
|
"is_list_of_p",
|
|
169
|
+
"is_list_p",
|
|
170
|
+
"is_lower_p",
|
|
171
|
+
"is_nan_p",
|
|
151
172
|
"is_none_p",
|
|
173
|
+
"is_not_empty_p",
|
|
152
174
|
"is_not_none_p",
|
|
153
175
|
"is_predicate_p",
|
|
154
|
-
"
|
|
176
|
+
"is_range_p",
|
|
155
177
|
"is_set_of_p",
|
|
178
|
+
"is_set_p",
|
|
156
179
|
"is_str_p",
|
|
180
|
+
"is_subset_p",
|
|
181
|
+
"is_superset_p",
|
|
182
|
+
"is_real_subset_p",
|
|
183
|
+
"is_real_superset_p",
|
|
184
|
+
"is_title_p",
|
|
157
185
|
"is_truthy_p",
|
|
158
|
-
"is_tuple_p",
|
|
159
186
|
"is_tuple_of_p",
|
|
187
|
+
"is_tuple_p",
|
|
188
|
+
"is_upper_p",
|
|
160
189
|
"is_uuid_p",
|
|
161
190
|
"lazy_p",
|
|
162
191
|
"le_p",
|
|
@@ -174,3 +203,14 @@ __all__ = [
|
|
|
174
203
|
"to_json",
|
|
175
204
|
"zero_p",
|
|
176
205
|
]
|
|
206
|
+
|
|
207
|
+
from predicate.str_predicates import (
|
|
208
|
+
is_alnum_p,
|
|
209
|
+
is_alpha_p,
|
|
210
|
+
is_ascii_p,
|
|
211
|
+
is_decimal_p,
|
|
212
|
+
is_identifier_p,
|
|
213
|
+
is_lower_p,
|
|
214
|
+
is_title_p,
|
|
215
|
+
is_upper_p,
|
|
216
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Iterable, override
|
|
3
|
+
|
|
4
|
+
from more_itertools import first
|
|
5
|
+
|
|
6
|
+
from predicate.predicate import Predicate
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class AllPredicate[T](Predicate[T]):
|
|
11
|
+
"""A predicate class that models the 'all' predicate."""
|
|
12
|
+
|
|
13
|
+
predicate: Predicate[T]
|
|
14
|
+
|
|
15
|
+
def __call__(self, iterable: Iterable[T]) -> bool:
|
|
16
|
+
return all(self.predicate(x) for x in iterable)
|
|
17
|
+
|
|
18
|
+
def __repr__(self) -> str:
|
|
19
|
+
return f"all({repr(self.predicate)})"
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
def explain_failure(self, iterable: Iterable[T]) -> dict:
|
|
23
|
+
fail = first(item for item in iterable if not self.predicate(item))
|
|
24
|
+
|
|
25
|
+
return {"result": False, "reason": f"Item '{fail}' didn't match predicate {repr(self.predicate)}"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Iterable, override
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class AnyPredicate[T](Predicate[T]):
|
|
9
|
+
"""A predicate class that models the 'any' predicate."""
|
|
10
|
+
|
|
11
|
+
predicate: Predicate[T]
|
|
12
|
+
|
|
13
|
+
def __call__(self, iterable: Iterable[T]) -> bool:
|
|
14
|
+
return any(self.predicate(x) for x in iterable)
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f"any({repr(self.predicate)})"
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def explain_failure(self, iterable: Iterable[T]) -> dict:
|
|
21
|
+
return {"result": False, "reason": f"No item matches predicate {repr(self.predicate)}"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Callable
|
|
2
|
+
from typing import Callable, override
|
|
3
3
|
|
|
4
4
|
from predicate.predicate import Predicate
|
|
5
5
|
|
|
@@ -16,3 +16,7 @@ class CompPredicate[S, T](Predicate[T]):
|
|
|
16
16
|
|
|
17
17
|
def __repr__(self) -> str:
|
|
18
18
|
return f"comp_p({repr(self.predicate)})"
|
|
19
|
+
|
|
20
|
+
@override
|
|
21
|
+
def explain_failure(self, x: S) -> dict:
|
|
22
|
+
return {"result": False, "predicate": self.predicate.explain(x)}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, override
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class DictOfPredicate[T](Predicate[T]):
|
|
9
|
+
"""A predicate class that models the dict_of predicate."""
|
|
10
|
+
|
|
11
|
+
key_value_predicates: list[tuple[Predicate, Predicate]]
|
|
12
|
+
|
|
13
|
+
def __init__(self, key_value_predicates: list[tuple[Predicate | str, Predicate]]):
|
|
14
|
+
def to_key_p(key_p: Predicate | str) -> Predicate:
|
|
15
|
+
from predicate.standard_predicates import eq_p
|
|
16
|
+
|
|
17
|
+
match key_p:
|
|
18
|
+
case str(s):
|
|
19
|
+
return eq_p(s)
|
|
20
|
+
case _:
|
|
21
|
+
return key_p
|
|
22
|
+
|
|
23
|
+
self.key_value_predicates = [(to_key_p(key_p), value_p) for key_p, value_p in key_value_predicates]
|
|
24
|
+
|
|
25
|
+
def __call__(self, x: Any) -> bool:
|
|
26
|
+
if not isinstance(x, dict):
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
if not x:
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
# For all values, a predicate must be True
|
|
33
|
+
for key, value in x.items():
|
|
34
|
+
if not any(key_p(key) and value_p(value) for key_p, value_p in self.key_value_predicates):
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
# All predicates must be True
|
|
38
|
+
for key_p, value_p in self.key_value_predicates:
|
|
39
|
+
if any(key_p(key) and not value_p(value) for key, value in x.items()):
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
def __repr__(self) -> str:
|
|
45
|
+
# TODO: show predicates
|
|
46
|
+
return "is_dict_of_p"
|
|
47
|
+
|
|
48
|
+
@override
|
|
49
|
+
def explain_failure(self, x: Any) -> dict:
|
|
50
|
+
# TODO: finish
|
|
51
|
+
return {"result": False, "key_value_predicates": []}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import override
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class EqPredicate[T](Predicate[T]):
|
|
9
|
+
"""A predicate class that models the 'eq' (=) predicate."""
|
|
10
|
+
|
|
11
|
+
v: T
|
|
12
|
+
|
|
13
|
+
def __call__(self, x: T) -> bool:
|
|
14
|
+
return x == self.v
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f"eq_p({self.v})"
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def explain_failure(self, x: T) -> dict:
|
|
21
|
+
return {"result": False, "reason": f"{x} is not equal to {self.v}"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Callable, override
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class FnPredicate[T](Predicate[T]):
|
|
9
|
+
"""A predicate class that can hold a function."""
|
|
10
|
+
|
|
11
|
+
predicate_fn: Callable[[T], bool]
|
|
12
|
+
|
|
13
|
+
def __call__(self, x: T) -> bool:
|
|
14
|
+
return self.predicate_fn(x)
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return "fn_p"
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def explain_failure(self, x: T) -> dict:
|
|
21
|
+
return {"result": False, "reason": f"Function returned False for value {x}"}
|
|
@@ -8,9 +8,18 @@ from more_itertools import first
|
|
|
8
8
|
from predicate.all_predicate import AllPredicate
|
|
9
9
|
from predicate.any_predicate import AnyPredicate
|
|
10
10
|
from predicate.comp_predicate import CompPredicate
|
|
11
|
+
from predicate.dict_of_predicate import DictOfPredicate
|
|
12
|
+
from predicate.eq_predicate import EqPredicate
|
|
13
|
+
from predicate.fn_predicate import FnPredicate
|
|
14
|
+
from predicate.ge_predicate import GePredicate
|
|
15
|
+
from predicate.gt_predicate import GtPredicate
|
|
11
16
|
from predicate.is_instance_predicate import IsInstancePredicate
|
|
17
|
+
from predicate.is_none_predicate import IsNonePredicate
|
|
12
18
|
from predicate.lazy_predicate import LazyPredicate, find_predicate_by_ref
|
|
19
|
+
from predicate.le_predicate import LePredicate
|
|
20
|
+
from predicate.lt_predicate import LtPredicate
|
|
13
21
|
from predicate.named_predicate import NamedPredicate
|
|
22
|
+
from predicate.ne_predicate import NePredicate
|
|
14
23
|
from predicate.optimizer.predicate_optimizer import optimize
|
|
15
24
|
from predicate.predicate import (
|
|
16
25
|
AlwaysFalsePredicate,
|
|
@@ -25,25 +34,24 @@ from predicate.predicate import (
|
|
|
25
34
|
)
|
|
26
35
|
from predicate.range_predicate import GeLePredicate, GeLtPredicate, GtLePredicate, GtLtPredicate
|
|
27
36
|
from predicate.root_predicate import RootPredicate, find_root_predicate
|
|
28
|
-
from predicate.
|
|
29
|
-
EqPredicate,
|
|
30
|
-
FnPredicate,
|
|
31
|
-
GePredicate,
|
|
32
|
-
GtPredicate,
|
|
37
|
+
from predicate.set_predicates import (
|
|
33
38
|
InPredicate,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
IsRealSubsetPredicate,
|
|
40
|
+
IsRealSupersetPredicate,
|
|
41
|
+
IsSubsetPredicate,
|
|
42
|
+
IsSupersetPredicate,
|
|
38
43
|
NotInPredicate,
|
|
39
44
|
)
|
|
40
45
|
from predicate.tee_predicate import TeePredicate
|
|
41
46
|
from predicate.this_predicate import ThisPredicate, find_this_predicate
|
|
47
|
+
from predicate.tuple_of_predicate import TupleOfPredicate
|
|
42
48
|
|
|
43
49
|
|
|
44
|
-
def to_dot(predicate: Predicate, predicate_string: str =
|
|
50
|
+
def to_dot(predicate: Predicate, predicate_string: str | None = None, show_optimized: bool = False):
|
|
45
51
|
"""Format predicate as a .dot file."""
|
|
46
|
-
|
|
52
|
+
label = predicate_string if predicate_string else repr(predicate)
|
|
53
|
+
|
|
54
|
+
graph_attr = {"label": label, "labelloc": "t"}
|
|
47
55
|
|
|
48
56
|
node_attr = {"shape": "rectangle", "style": "filled", "fillcolor": "#B7D7A8"}
|
|
49
57
|
|
|
@@ -61,46 +69,53 @@ def to_dot(predicate: Predicate, predicate_string: str = "", show_optimized: boo
|
|
|
61
69
|
return dot
|
|
62
70
|
|
|
63
71
|
|
|
64
|
-
def
|
|
72
|
+
def set_to_str(v: set) -> str:
|
|
73
|
+
# TODO: truncate if too many items.
|
|
74
|
+
items = ", ".join(str(item) for item in v)
|
|
75
|
+
return f"{{{items}}}"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def render(dot, predicate: Predicate, node_nr: count):
|
|
65
79
|
node_predicate_mapping: dict[str, Predicate] = {}
|
|
66
80
|
|
|
67
|
-
def _add_node(name: str, *, label: str, predicate: Predicate):
|
|
81
|
+
def _add_node(name: str, *, label: str, predicate: Predicate | None) -> str:
|
|
68
82
|
node = next(node_nr)
|
|
69
83
|
unique_name = f"{name}_{node}"
|
|
70
84
|
dot.node(unique_name, label=label)
|
|
71
|
-
|
|
85
|
+
if predicate:
|
|
86
|
+
node_predicate_mapping[unique_name] = predicate
|
|
72
87
|
return unique_name
|
|
73
88
|
|
|
89
|
+
def _add_node_left_right(name: str, *, label: str, predicate: Predicate, left: Predicate, right: Predicate) -> str:
|
|
90
|
+
node = _add_node(name, label=label, predicate=predicate)
|
|
91
|
+
dot.edge(node, to_value(left))
|
|
92
|
+
dot.edge(node, to_value(right))
|
|
93
|
+
|
|
94
|
+
return node
|
|
95
|
+
|
|
96
|
+
def _add_node_with_child(name: str, *, label: str, predicate: Predicate, child: Predicate) -> str:
|
|
97
|
+
node = _add_node(name, label=label, predicate=predicate)
|
|
98
|
+
dot.edge(node, to_value(child))
|
|
99
|
+
return node
|
|
100
|
+
|
|
74
101
|
def to_value(predicate: Predicate):
|
|
75
102
|
add_node = partial(_add_node, predicate=predicate)
|
|
103
|
+
add_node_left_right = partial(_add_node_left_right, predicate=predicate)
|
|
104
|
+
add_node_with_child = partial(_add_node_with_child, predicate=predicate)
|
|
76
105
|
|
|
77
106
|
match predicate:
|
|
78
107
|
case AllPredicate(all_predicate):
|
|
79
|
-
|
|
80
|
-
child = to_value(all_predicate)
|
|
81
|
-
dot.edge(node, child)
|
|
82
|
-
return node
|
|
108
|
+
return add_node_with_child("all", label="∀", child=all_predicate)
|
|
83
109
|
case AlwaysFalsePredicate():
|
|
84
110
|
return add_node("F", label="false")
|
|
85
111
|
case AlwaysTruePredicate():
|
|
86
112
|
return add_node("T", label="true")
|
|
87
113
|
case AndPredicate(left, right):
|
|
88
|
-
|
|
89
|
-
left_node = to_value(left)
|
|
90
|
-
right_node = to_value(right)
|
|
91
|
-
dot.edge(node, left_node)
|
|
92
|
-
dot.edge(node, right_node)
|
|
93
|
-
return node
|
|
114
|
+
return add_node_left_right("and", label="∧", left=left, right=right)
|
|
94
115
|
case AnyPredicate(any_predicate):
|
|
95
|
-
|
|
96
|
-
child = to_value(any_predicate)
|
|
97
|
-
dot.edge(node, child)
|
|
98
|
-
return node
|
|
116
|
+
return add_node_with_child("any", label="∃", child=any_predicate)
|
|
99
117
|
case CompPredicate(_fn, comp_predicate):
|
|
100
|
-
|
|
101
|
-
child = to_value(comp_predicate)
|
|
102
|
-
dot.edge(node, child)
|
|
103
|
-
return node
|
|
118
|
+
return add_node_with_child("comp", label="f", child=comp_predicate)
|
|
104
119
|
case EqPredicate(v):
|
|
105
120
|
return add_node("eq", label=f"x = {v}")
|
|
106
121
|
case IsFalsyPredicate():
|
|
@@ -119,17 +134,32 @@ def render(dot, predicate: Predicate, node_nr):
|
|
|
119
134
|
case GtPredicate(v):
|
|
120
135
|
return add_node("gt", label=f"x > {v}")
|
|
121
136
|
case GtLePredicate(upper, lower):
|
|
122
|
-
return add_node("
|
|
137
|
+
return add_node("gtle", label=f"{lower} ≤ x ≤ {upper}")
|
|
123
138
|
case GtLtPredicate(upper, lower):
|
|
124
|
-
return add_node("
|
|
139
|
+
return add_node("gtlt", label=f"{lower} ≤ x < {upper}")
|
|
125
140
|
case InPredicate(v):
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
return add_node("in", label=f"x ∈ {set_to_str(v)}")
|
|
142
|
+
case DictOfPredicate(key_value_predicates):
|
|
143
|
+
node = add_node("dict_of", label="is_dict_of")
|
|
144
|
+
for key, value in key_value_predicates:
|
|
145
|
+
kv = _add_node("kv", label="kv", predicate=None)
|
|
146
|
+
dot.edge(node, kv)
|
|
147
|
+
dot.edge(kv, to_value(key), label="key")
|
|
148
|
+
dot.edge(kv, to_value(value), label="value")
|
|
149
|
+
return node
|
|
128
150
|
case IsInstancePredicate(klass):
|
|
129
151
|
name = klass[0].__name__ # type: ignore
|
|
130
152
|
return add_node("instance", label=f"is_{name}_p")
|
|
131
153
|
case IsNonePredicate():
|
|
132
154
|
return add_node("none", label="x = None")
|
|
155
|
+
case IsRealSubsetPredicate(v):
|
|
156
|
+
return add_node("real_subset", label=f"x ⊂ {set_to_str(v)}")
|
|
157
|
+
case IsSubsetPredicate(v):
|
|
158
|
+
return add_node("subset", label=f"x ⊆ {set_to_str(v)}")
|
|
159
|
+
case IsRealSupersetPredicate(v):
|
|
160
|
+
return add_node("real_superset", label=f"x ⊃ {set_to_str(v)}")
|
|
161
|
+
case IsSupersetPredicate(v):
|
|
162
|
+
return add_node("superset", label=f"x ⊇ {set_to_str(v)}")
|
|
133
163
|
case LazyPredicate(ref):
|
|
134
164
|
return add_node("lazy", label=ref)
|
|
135
165
|
case LePredicate(v):
|
|
@@ -139,35 +169,26 @@ def render(dot, predicate: Predicate, node_nr):
|
|
|
139
169
|
case NamedPredicate(name):
|
|
140
170
|
return add_node("named", label=name)
|
|
141
171
|
case NotInPredicate(v):
|
|
142
|
-
|
|
143
|
-
return add_node("in", label=f"x ∉ {{{items}}}")
|
|
172
|
+
return add_node("in", label=f"x ∉ {set_to_str(v)}")
|
|
144
173
|
case NePredicate(v):
|
|
145
174
|
return add_node("ne", label=f"x ≠ {v}")
|
|
146
175
|
case NotPredicate(not_predicate):
|
|
147
|
-
|
|
148
|
-
node = add_node("not", label="¬")
|
|
149
|
-
dot.edge(node, child)
|
|
150
|
-
return node
|
|
176
|
+
return add_node_with_child("not", label="¬", child=not_predicate)
|
|
151
177
|
case OrPredicate(left, right):
|
|
152
|
-
|
|
153
|
-
left_node = to_value(left)
|
|
154
|
-
right_node = to_value(right)
|
|
155
|
-
dot.edge(node, left_node)
|
|
156
|
-
dot.edge(node, right_node)
|
|
157
|
-
return node
|
|
178
|
+
return add_node_left_right("or", label="∨", left=left, right=right)
|
|
158
179
|
case RootPredicate():
|
|
159
180
|
return add_node("root", label="root")
|
|
160
181
|
case TeePredicate():
|
|
161
182
|
return add_node("tee", label="tee")
|
|
162
183
|
case ThisPredicate():
|
|
163
184
|
return add_node("this", label="this")
|
|
164
|
-
case
|
|
165
|
-
node = add_node("
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
dot.edge(node, left_node)
|
|
169
|
-
dot.edge(node, right_node)
|
|
185
|
+
case TupleOfPredicate(predicates):
|
|
186
|
+
node = add_node("tuple_of", label="is_tuple_of")
|
|
187
|
+
for tuple_predicate in predicates:
|
|
188
|
+
dot.edge(node, to_value(tuple_predicate))
|
|
170
189
|
return node
|
|
190
|
+
case XorPredicate(left, right):
|
|
191
|
+
return add_node_left_right("xor", label="⊻", left=left, right=right)
|
|
171
192
|
case _:
|
|
172
193
|
raise ValueError(f"Unknown predicate type {predicate}")
|
|
173
194
|
|
|
@@ -176,7 +197,7 @@ def render(dot, predicate: Predicate, node_nr):
|
|
|
176
197
|
render_lazy_references(dot, node_predicate_mapping)
|
|
177
198
|
|
|
178
199
|
|
|
179
|
-
def render_lazy_references(dot, node_predicate_mapping):
|
|
200
|
+
def render_lazy_references(dot, node_predicate_mapping) -> None:
|
|
180
201
|
def find_in_mapping(lookup: Predicate) -> str:
|
|
181
202
|
return first(node for node, predicate in node_predicate_mapping.items() if predicate == lookup)
|
|
182
203
|
|
|
@@ -199,14 +220,14 @@ def render_lazy_references(dot, node_predicate_mapping):
|
|
|
199
220
|
add_dashed_line(node, this)
|
|
200
221
|
|
|
201
222
|
|
|
202
|
-
def render_original(dot, predicate: Predicate, node_nr):
|
|
223
|
+
def render_original(dot, predicate: Predicate, node_nr) -> None:
|
|
203
224
|
with dot.subgraph(name="cluster_original") as original:
|
|
204
225
|
original.attr(style="filled", color="lightgrey")
|
|
205
226
|
original.attr(label="Original predicate")
|
|
206
227
|
render(original, predicate, node_nr)
|
|
207
228
|
|
|
208
229
|
|
|
209
|
-
def render_optimized(dot, predicate: Predicate, node_nr):
|
|
230
|
+
def render_optimized(dot, predicate: Predicate, node_nr) -> None:
|
|
210
231
|
optimized_predicate = optimize(predicate)
|
|
211
232
|
|
|
212
233
|
with dot.subgraph(name="cluster_optimized") as optimized:
|
|
@@ -2,15 +2,15 @@ from typing import Any
|
|
|
2
2
|
|
|
3
3
|
from predicate.all_predicate import AllPredicate
|
|
4
4
|
from predicate.any_predicate import AnyPredicate
|
|
5
|
+
from predicate.fn_predicate import FnPredicate
|
|
5
6
|
from predicate.named_predicate import NamedPredicate
|
|
7
|
+
from predicate.ne_predicate import NePredicate
|
|
6
8
|
from predicate.predicate import (
|
|
7
9
|
AlwaysFalsePredicate,
|
|
8
10
|
AlwaysTruePredicate,
|
|
9
11
|
AndPredicate,
|
|
10
|
-
FnPredicate,
|
|
11
12
|
IsFalsyPredicate,
|
|
12
13
|
IsTruthyPredicate,
|
|
13
|
-
NePredicate,
|
|
14
14
|
NotPredicate,
|
|
15
15
|
OrPredicate,
|
|
16
16
|
Predicate,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import override
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import ConstrainedT, Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class GePredicate[T](Predicate[T]):
|
|
9
|
+
"""A predicate class that models the 'ge' (>=) predicate."""
|
|
10
|
+
|
|
11
|
+
v: ConstrainedT
|
|
12
|
+
|
|
13
|
+
def __call__(self, x: T) -> bool:
|
|
14
|
+
return x >= self.v
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f"ge_p({self.v})"
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def explain_failure(self, x: T) -> dict:
|
|
21
|
+
return {"result": False, "reason": f"{x} is not greater or equal to {self.v}"}
|