py-predicate 0.3__tar.gz → 0.5__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.3 → py_predicate-0.5}/PKG-INFO +5 -3
- {py_predicate-0.3 → py_predicate-0.5}/README.md +2 -2
- {py_predicate-0.3 → py_predicate-0.5}/predicate/__init__.py +74 -13
- py_predicate-0.5/predicate/all_predicate.py +17 -0
- py_predicate-0.5/predicate/any_predicate.py +17 -0
- py_predicate-0.5/predicate/constructor/construct.py +49 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/formatter/format_dot.py +65 -51
- {py_predicate-0.3 → py_predicate-0.5}/predicate/formatter/format_json.py +6 -3
- py_predicate-0.5/predicate/generator/__init__.py +4 -0
- py_predicate-0.5/predicate/generator/generate_false.py +176 -0
- py_predicate-0.5/predicate/generator/generate_true.py +285 -0
- py_predicate-0.5/predicate/generator/helpers.py +85 -0
- py_predicate-0.5/predicate/has_key_predicate.py +16 -0
- py_predicate-0.5/predicate/has_length_predicate.py +19 -0
- py_predicate-0.5/predicate/implies.py +109 -0
- py_predicate-0.5/predicate/ip_address_predicates.py +47 -0
- py_predicate-0.5/predicate/is_instance_predicate.py +17 -0
- py_predicate-0.5/predicate/named_predicate.py +17 -0
- py_predicate-0.5/predicate/optimizer/__init__.py +0 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/optimizer/all_optimizer.py +4 -3
- {py_predicate-0.3 → py_predicate-0.5}/predicate/optimizer/and_optimizer.py +39 -19
- {py_predicate-0.3 → py_predicate-0.5}/predicate/optimizer/any_optimizer.py +6 -4
- py_predicate-0.5/predicate/optimizer/in_optimizer.py +24 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/optimizer/not_optimizer.py +2 -2
- {py_predicate-0.3 → py_predicate-0.5}/predicate/optimizer/or_optimizer.py +20 -13
- {py_predicate-0.3 → py_predicate-0.5}/predicate/optimizer/predicate_optimizer.py +8 -4
- {py_predicate-0.3 → py_predicate-0.5}/predicate/optimizer/xor_optimizer.py +11 -4
- {py_predicate-0.3 → py_predicate-0.5}/predicate/parser.py +3 -2
- {py_predicate-0.3 → py_predicate-0.5}/predicate/predicate.py +11 -150
- py_predicate-0.5/predicate/property_predicate.py +20 -0
- py_predicate-0.5/predicate/range_predicate.py +59 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/regex_predicate.py +1 -0
- py_predicate-0.5/predicate/set_of_predicate.py +16 -0
- py_predicate-0.5/predicate/set_predicates.py +120 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/standard_predicates.py +115 -32
- py_predicate-0.5/predicate/str_predicates.py +55 -0
- py_predicate-0.5/predicate/tee_predicate.py +18 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/this_predicate.py +5 -1
- {py_predicate-0.3 → py_predicate-0.5}/predicate/truth_table.py +2 -1
- py_predicate-0.5/predicate/tuple_of_predicate.py +19 -0
- {py_predicate-0.3 → py_predicate-0.5}/pyproject.toml +5 -1
- py_predicate-0.3/predicate/implies.py +0 -53
- py_predicate-0.3/predicate/optimizer/in_optimizer.py +0 -13
- {py_predicate-0.3 → py_predicate-0.5}/predicate/comp_predicate.py +0 -0
- {py_predicate-0.3/predicate/formatter → py_predicate-0.5/predicate/constructor}/__init__.py +0 -0
- {py_predicate-0.3/predicate/optimizer → py_predicate-0.5/predicate/formatter}/__init__.py +0 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/lazy_predicate.py +0 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/negate.py +0 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/optimizer/rules.py +0 -0
- {py_predicate-0.3 → py_predicate-0.5}/predicate/root_predicate.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: py_predicate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5
|
|
4
4
|
Summary: Module to create composable predicates
|
|
5
5
|
Author-email: Maurits Rijk <maurits.rijk@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -21,6 +21,7 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Requires-Dist: exrex
|
|
24
25
|
Requires-Dist: graphviz
|
|
25
26
|
Requires-Dist: lark
|
|
26
27
|
Requires-Dist: more-itertools
|
|
@@ -32,6 +33,7 @@ Requires-Dist: black ; extra == "test"
|
|
|
32
33
|
Requires-Dist: mypy ; extra == "test"
|
|
33
34
|
Requires-Dist: mypy_extensions ; extra == "test"
|
|
34
35
|
Requires-Dist: pytest ; extra == "test"
|
|
36
|
+
Requires-Dist: pytest-benchmark ; extra == "test"
|
|
35
37
|
Requires-Dist: pytest-cov ; extra == "test"
|
|
36
38
|
Requires-Dist: ruff ; extra == "test"
|
|
37
39
|
Project-URL: Documentation, https://mrijk.github.io/py-predicate/
|
|
@@ -89,9 +91,9 @@ either a string, or a list of data that can again either be a string or a list o
|
|
|
89
91
|
data. Ad infinitum.
|
|
90
92
|
|
|
91
93
|
```python
|
|
92
|
-
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
|
|
93
95
|
|
|
94
|
-
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))
|
|
95
97
|
```
|
|
96
98
|
|
|
97
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.
|
|
@@ -2,28 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
__version__ = "0.0.1"
|
|
4
4
|
|
|
5
|
+
from predicate.all_predicate import AllPredicate
|
|
6
|
+
from predicate.any_predicate import AnyPredicate
|
|
5
7
|
from predicate.formatter.format_dot import to_dot
|
|
6
8
|
from predicate.formatter.format_json import to_json
|
|
9
|
+
from predicate.generator.generate_false import generate_false
|
|
10
|
+
from predicate.generator.generate_true import generate_true
|
|
7
11
|
from predicate.optimizer.predicate_optimizer import can_optimize, optimize
|
|
8
12
|
from predicate.predicate import (
|
|
9
|
-
AllPredicate,
|
|
10
13
|
AlwaysFalsePredicate,
|
|
11
14
|
AlwaysTruePredicate,
|
|
12
15
|
AndPredicate,
|
|
13
|
-
AnyPredicate,
|
|
14
16
|
EqPredicate,
|
|
15
17
|
FnPredicate,
|
|
16
18
|
GePredicate,
|
|
17
19
|
GtPredicate,
|
|
18
|
-
InPredicate,
|
|
19
20
|
IsEmptyPredicate,
|
|
20
|
-
IsInstancePredicate,
|
|
21
21
|
IsNonePredicate,
|
|
22
22
|
IsNotNonePredicate,
|
|
23
23
|
LePredicate,
|
|
24
24
|
LtPredicate,
|
|
25
25
|
NePredicate,
|
|
26
|
-
NotInPredicate,
|
|
27
26
|
NotPredicate,
|
|
28
27
|
OrPredicate,
|
|
29
28
|
Predicate,
|
|
@@ -32,6 +31,16 @@ from predicate.predicate import (
|
|
|
32
31
|
always_true_p,
|
|
33
32
|
is_empty_p,
|
|
34
33
|
)
|
|
34
|
+
from predicate.set_predicates import (
|
|
35
|
+
InPredicate,
|
|
36
|
+
NotInPredicate,
|
|
37
|
+
in_p,
|
|
38
|
+
is_real_subset_p,
|
|
39
|
+
is_real_superset_p,
|
|
40
|
+
is_subset_p,
|
|
41
|
+
is_superset_p,
|
|
42
|
+
not_in_p,
|
|
43
|
+
)
|
|
35
44
|
from predicate.standard_predicates import (
|
|
36
45
|
all_p,
|
|
37
46
|
any_p,
|
|
@@ -40,16 +49,24 @@ from predicate.standard_predicates import (
|
|
|
40
49
|
eq_p,
|
|
41
50
|
eq_true_p,
|
|
42
51
|
fn_p,
|
|
52
|
+
ge_le_p,
|
|
53
|
+
ge_lt_p,
|
|
43
54
|
ge_p,
|
|
55
|
+
gt_le_p,
|
|
56
|
+
gt_lt_p,
|
|
44
57
|
gt_p,
|
|
45
|
-
|
|
58
|
+
has_length_p,
|
|
46
59
|
is_bool_p,
|
|
47
60
|
is_callable_p,
|
|
48
61
|
is_complex_p,
|
|
62
|
+
is_container_p,
|
|
49
63
|
is_datetime_p,
|
|
50
64
|
is_dict_p,
|
|
51
65
|
is_falsy_p,
|
|
66
|
+
is_finite_p,
|
|
52
67
|
is_float_p,
|
|
68
|
+
is_hashable_p,
|
|
69
|
+
is_inf_p,
|
|
53
70
|
is_instance_p,
|
|
54
71
|
is_int_p,
|
|
55
72
|
is_iterable_of_p,
|
|
@@ -59,9 +76,11 @@ from predicate.standard_predicates import (
|
|
|
59
76
|
is_none_p,
|
|
60
77
|
is_not_none_p,
|
|
61
78
|
is_predicate_p,
|
|
79
|
+
is_range_p,
|
|
62
80
|
is_set_of_p,
|
|
63
81
|
is_set_p,
|
|
64
82
|
is_str_p,
|
|
83
|
+
is_truthy_p,
|
|
65
84
|
is_tuple_of_p,
|
|
66
85
|
is_tuple_p,
|
|
67
86
|
is_uuid_p,
|
|
@@ -69,10 +88,13 @@ from predicate.standard_predicates import (
|
|
|
69
88
|
le_p,
|
|
70
89
|
lt_p,
|
|
71
90
|
ne_p,
|
|
72
|
-
|
|
91
|
+
neg_p,
|
|
92
|
+
pos_p,
|
|
73
93
|
regex_p,
|
|
74
94
|
root_p,
|
|
95
|
+
tee_p,
|
|
75
96
|
this_p,
|
|
97
|
+
zero_p,
|
|
76
98
|
)
|
|
77
99
|
|
|
78
100
|
__all__ = [
|
|
@@ -87,7 +109,6 @@ __all__ = [
|
|
|
87
109
|
"GtPredicate",
|
|
88
110
|
"InPredicate",
|
|
89
111
|
"IsEmptyPredicate",
|
|
90
|
-
"IsInstancePredicate",
|
|
91
112
|
"IsNonePredicate",
|
|
92
113
|
"IsNotNonePredicate",
|
|
93
114
|
"LePredicate",
|
|
@@ -107,42 +128,82 @@ __all__ = [
|
|
|
107
128
|
"eq_false_p",
|
|
108
129
|
"eq_p",
|
|
109
130
|
"eq_true_p",
|
|
110
|
-
"is_falsy_p",
|
|
111
131
|
"fn_p",
|
|
132
|
+
"ge_le_p",
|
|
133
|
+
"ge_lt_p",
|
|
112
134
|
"ge_p",
|
|
135
|
+
"generate_false",
|
|
136
|
+
"generate_true",
|
|
137
|
+
"gt_le_p",
|
|
138
|
+
"gt_lt_p",
|
|
113
139
|
"gt_p",
|
|
140
|
+
"has_length_p",
|
|
114
141
|
"in_p",
|
|
142
|
+
"is_alnum_p",
|
|
143
|
+
"is_alpha_p",
|
|
144
|
+
"is_ascii_p",
|
|
115
145
|
"is_bool_p",
|
|
116
146
|
"is_callable_p",
|
|
117
147
|
"is_complex_p",
|
|
148
|
+
"is_container_p",
|
|
118
149
|
"is_datetime_p",
|
|
150
|
+
"is_decimal_p",
|
|
119
151
|
"is_dict_p",
|
|
120
152
|
"is_empty_p",
|
|
153
|
+
"is_falsy_p",
|
|
154
|
+
"is_finite_p",
|
|
121
155
|
"is_float_p",
|
|
156
|
+
"is_hashable_p",
|
|
157
|
+
"is_identifier_p",
|
|
158
|
+
"is_inf_p",
|
|
122
159
|
"is_instance_p",
|
|
123
160
|
"is_int_p",
|
|
124
|
-
"is_iterable_p",
|
|
125
161
|
"is_iterable_of_p",
|
|
126
|
-
"
|
|
162
|
+
"is_iterable_p",
|
|
127
163
|
"is_list_of_p",
|
|
164
|
+
"is_list_p",
|
|
165
|
+
"is_lower_p",
|
|
128
166
|
"is_none_p",
|
|
129
167
|
"is_not_none_p",
|
|
130
168
|
"is_predicate_p",
|
|
131
|
-
"
|
|
169
|
+
"is_range_p",
|
|
132
170
|
"is_set_of_p",
|
|
171
|
+
"is_set_p",
|
|
133
172
|
"is_str_p",
|
|
134
|
-
"
|
|
173
|
+
"is_subset_p",
|
|
174
|
+
"is_superset_p",
|
|
175
|
+
"is_real_subset_p",
|
|
176
|
+
"is_real_superset_p",
|
|
177
|
+
"is_title_p",
|
|
178
|
+
"is_truthy_p",
|
|
135
179
|
"is_tuple_of_p",
|
|
180
|
+
"is_tuple_p",
|
|
181
|
+
"is_upper_p",
|
|
136
182
|
"is_uuid_p",
|
|
137
183
|
"lazy_p",
|
|
138
184
|
"le_p",
|
|
139
185
|
"lt_p",
|
|
140
186
|
"ne_p",
|
|
187
|
+
"neg_p",
|
|
141
188
|
"not_in_p",
|
|
142
189
|
"optimize",
|
|
190
|
+
"pos_p",
|
|
143
191
|
"regex_p",
|
|
144
192
|
"root_p",
|
|
193
|
+
"tee_p",
|
|
145
194
|
"this_p",
|
|
146
195
|
"to_dot",
|
|
147
196
|
"to_json",
|
|
197
|
+
"zero_p",
|
|
148
198
|
]
|
|
199
|
+
|
|
200
|
+
from predicate.str_predicates import (
|
|
201
|
+
is_alnum_p,
|
|
202
|
+
is_alpha_p,
|
|
203
|
+
is_ascii_p,
|
|
204
|
+
is_decimal_p,
|
|
205
|
+
is_identifier_p,
|
|
206
|
+
is_lower_p,
|
|
207
|
+
is_title_p,
|
|
208
|
+
is_upper_p,
|
|
209
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Iterable
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class AllPredicate[T](Predicate[T]):
|
|
9
|
+
"""A predicate class that models the 'all' predicate."""
|
|
10
|
+
|
|
11
|
+
predicate: Predicate[T]
|
|
12
|
+
|
|
13
|
+
def __call__(self, iterable: Iterable[T]) -> bool:
|
|
14
|
+
return all(self.predicate(x) for x in iterable)
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f"all({repr(self.predicate)})"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Iterable
|
|
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)})"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import Iterator
|
|
2
|
+
|
|
3
|
+
from more_itertools import gray_product
|
|
4
|
+
|
|
5
|
+
from predicate import is_datetime_p, is_falsy_p, is_float_p, is_int_p, is_not_none_p, is_set_p, is_str_p, is_truthy_p
|
|
6
|
+
from predicate.predicate import Predicate, always_false_p, always_true_p
|
|
7
|
+
from predicate.standard_predicates import all_p, is_bool_p, is_dict_p, is_list_p, is_none_p
|
|
8
|
+
|
|
9
|
+
# TODO: this is very much work under construction (pun intended) and not ready for public consumption
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def construct(false_set: list, true_set: list) -> Iterator[Predicate]:
|
|
13
|
+
predicates = list(initial_predicates())
|
|
14
|
+
|
|
15
|
+
while True:
|
|
16
|
+
for predicate in predicates:
|
|
17
|
+
all_true = all_p(predicate)
|
|
18
|
+
all_false = all_p(~predicate)
|
|
19
|
+
if all_true(true_set) and all_false(false_set):
|
|
20
|
+
yield predicate
|
|
21
|
+
|
|
22
|
+
predicates = list(create_mutations(predicates))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def create_mutations(candidates: list[Predicate]) -> Iterator[Predicate]:
|
|
26
|
+
pairs = gray_product(candidates, candidates)
|
|
27
|
+
for pair in pairs:
|
|
28
|
+
left, right = pair
|
|
29
|
+
if left != right:
|
|
30
|
+
yield left | right
|
|
31
|
+
yield left & right
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def initial_predicates() -> Iterator[Predicate]:
|
|
35
|
+
# TODO: probably import from __init__
|
|
36
|
+
yield always_false_p
|
|
37
|
+
yield always_true_p
|
|
38
|
+
yield is_bool_p
|
|
39
|
+
yield is_datetime_p
|
|
40
|
+
yield is_dict_p
|
|
41
|
+
yield is_falsy_p
|
|
42
|
+
yield is_float_p
|
|
43
|
+
yield is_int_p
|
|
44
|
+
yield is_list_p
|
|
45
|
+
yield is_none_p
|
|
46
|
+
yield is_not_none_p
|
|
47
|
+
yield is_set_p
|
|
48
|
+
yield is_str_p
|
|
49
|
+
yield is_truthy_p
|
|
@@ -5,8 +5,12 @@ from itertools import count
|
|
|
5
5
|
import graphviz # type: ignore
|
|
6
6
|
from more_itertools import first
|
|
7
7
|
|
|
8
|
+
from predicate.all_predicate import AllPredicate
|
|
9
|
+
from predicate.any_predicate import AnyPredicate
|
|
8
10
|
from predicate.comp_predicate import CompPredicate
|
|
11
|
+
from predicate.is_instance_predicate import IsInstancePredicate
|
|
9
12
|
from predicate.lazy_predicate import LazyPredicate, find_predicate_by_ref
|
|
13
|
+
from predicate.named_predicate import NamedPredicate
|
|
10
14
|
from predicate.optimizer.predicate_optimizer import optimize
|
|
11
15
|
from predicate.predicate import (
|
|
12
16
|
AlwaysFalsePredicate,
|
|
@@ -14,29 +18,32 @@ from predicate.predicate import (
|
|
|
14
18
|
AndPredicate,
|
|
15
19
|
IsFalsyPredicate,
|
|
16
20
|
IsTruthyPredicate,
|
|
17
|
-
NamedPredicate,
|
|
18
21
|
NotPredicate,
|
|
19
22
|
OrPredicate,
|
|
20
23
|
Predicate,
|
|
21
24
|
XorPredicate,
|
|
22
25
|
)
|
|
26
|
+
from predicate.range_predicate import GeLePredicate, GeLtPredicate, GtLePredicate, GtLtPredicate
|
|
23
27
|
from predicate.root_predicate import RootPredicate, find_root_predicate
|
|
28
|
+
from predicate.set_predicates import (
|
|
29
|
+
InPredicate,
|
|
30
|
+
IsRealSubsetPredicate,
|
|
31
|
+
IsRealSupersetPredicate,
|
|
32
|
+
IsSubsetPredicate,
|
|
33
|
+
IsSupersetPredicate,
|
|
34
|
+
NotInPredicate,
|
|
35
|
+
)
|
|
24
36
|
from predicate.standard_predicates import (
|
|
25
|
-
AllPredicate,
|
|
26
|
-
AnyPredicate,
|
|
27
37
|
EqPredicate,
|
|
28
38
|
FnPredicate,
|
|
29
39
|
GePredicate,
|
|
30
40
|
GtPredicate,
|
|
31
|
-
InPredicate,
|
|
32
|
-
IsInstancePredicate,
|
|
33
41
|
IsNonePredicate,
|
|
34
42
|
LePredicate,
|
|
35
43
|
LtPredicate,
|
|
36
44
|
NePredicate,
|
|
37
|
-
NotInPredicate,
|
|
38
|
-
PredicateFactory,
|
|
39
45
|
)
|
|
46
|
+
from predicate.tee_predicate import TeePredicate
|
|
40
47
|
from predicate.this_predicate import ThisPredicate, find_this_predicate
|
|
41
48
|
|
|
42
49
|
|
|
@@ -60,46 +67,52 @@ def to_dot(predicate: Predicate, predicate_string: str = "", show_optimized: boo
|
|
|
60
67
|
return dot
|
|
61
68
|
|
|
62
69
|
|
|
70
|
+
def set_to_str(v: set) -> str:
|
|
71
|
+
# TODO: truncate if too many items.
|
|
72
|
+
items = ", ".join(str(item) for item in v)
|
|
73
|
+
return f"{{{items}}}"
|
|
74
|
+
|
|
75
|
+
|
|
63
76
|
def render(dot, predicate: Predicate, node_nr):
|
|
64
77
|
node_predicate_mapping: dict[str, Predicate] = {}
|
|
65
78
|
|
|
66
|
-
def _add_node(name: str, *, label: str, predicate: Predicate):
|
|
79
|
+
def _add_node(name: str, *, label: str, predicate: Predicate) -> str:
|
|
67
80
|
node = next(node_nr)
|
|
68
81
|
unique_name = f"{name}_{node}"
|
|
69
82
|
dot.node(unique_name, label=label)
|
|
70
83
|
node_predicate_mapping[unique_name] = predicate
|
|
71
84
|
return unique_name
|
|
72
85
|
|
|
86
|
+
def _add_node_left_right(name: str, *, label: str, predicate: Predicate, left: Predicate, right: Predicate) -> str:
|
|
87
|
+
node = _add_node(name, label=label, predicate=predicate)
|
|
88
|
+
dot.edge(node, to_value(left))
|
|
89
|
+
dot.edge(node, to_value(right))
|
|
90
|
+
|
|
91
|
+
return node
|
|
92
|
+
|
|
93
|
+
def _add_node_with_child(name: str, *, label: str, predicate: Predicate, child: Predicate) -> str:
|
|
94
|
+
node = _add_node(name, label=label, predicate=predicate)
|
|
95
|
+
dot.edge(node, to_value(child))
|
|
96
|
+
return node
|
|
97
|
+
|
|
73
98
|
def to_value(predicate: Predicate):
|
|
74
99
|
add_node = partial(_add_node, predicate=predicate)
|
|
100
|
+
add_node_left_right = partial(_add_node_left_right, predicate=predicate)
|
|
101
|
+
add_node_with_child = partial(_add_node_with_child, predicate=predicate)
|
|
75
102
|
|
|
76
103
|
match predicate:
|
|
77
104
|
case AllPredicate(all_predicate):
|
|
78
|
-
|
|
79
|
-
child = to_value(all_predicate)
|
|
80
|
-
dot.edge(node, child)
|
|
81
|
-
return node
|
|
105
|
+
return add_node_with_child("all", label="∀", child=all_predicate)
|
|
82
106
|
case AlwaysFalsePredicate():
|
|
83
107
|
return add_node("F", label="false")
|
|
84
108
|
case AlwaysTruePredicate():
|
|
85
109
|
return add_node("T", label="true")
|
|
86
110
|
case AndPredicate(left, right):
|
|
87
|
-
|
|
88
|
-
left_node = to_value(left)
|
|
89
|
-
right_node = to_value(right)
|
|
90
|
-
dot.edge(node, left_node)
|
|
91
|
-
dot.edge(node, right_node)
|
|
92
|
-
return node
|
|
111
|
+
return add_node_left_right("and", label="∧", left=left, right=right)
|
|
93
112
|
case AnyPredicate(any_predicate):
|
|
94
|
-
|
|
95
|
-
child = to_value(any_predicate)
|
|
96
|
-
dot.edge(node, child)
|
|
97
|
-
return node
|
|
113
|
+
return add_node_with_child("any", label="∃", child=any_predicate)
|
|
98
114
|
case CompPredicate(_fn, comp_predicate):
|
|
99
|
-
|
|
100
|
-
child = to_value(comp_predicate)
|
|
101
|
-
dot.edge(node, child)
|
|
102
|
-
return node
|
|
115
|
+
return add_node_with_child("comp", label="f", child=comp_predicate)
|
|
103
116
|
case EqPredicate(v):
|
|
104
117
|
return add_node("eq", label=f"x = {v}")
|
|
105
118
|
case IsFalsyPredicate():
|
|
@@ -111,16 +124,31 @@ def render(dot, predicate: Predicate, node_nr):
|
|
|
111
124
|
return add_node("fn", label=f"fn: {name}")
|
|
112
125
|
case GePredicate(v):
|
|
113
126
|
return add_node("ge", label=f"x ≥ {v}")
|
|
127
|
+
case GeLePredicate(upper, lower):
|
|
128
|
+
return add_node("gele", label=f"{lower} ≤ x ≤ {upper}")
|
|
129
|
+
case GeLtPredicate(upper, lower):
|
|
130
|
+
return add_node("gelt", label=f"{lower} ≤ x < {upper}")
|
|
114
131
|
case GtPredicate(v):
|
|
115
132
|
return add_node("gt", label=f"x > {v}")
|
|
133
|
+
case GtLePredicate(upper, lower):
|
|
134
|
+
return add_node("gtle", label=f"{lower} ≤ x ≤ {upper}")
|
|
135
|
+
case GtLtPredicate(upper, lower):
|
|
136
|
+
return add_node("gtlt", label=f"{lower} ≤ x < {upper}")
|
|
116
137
|
case InPredicate(v):
|
|
117
|
-
|
|
118
|
-
return add_node("in", label=f"x ∈ {{{items}}}")
|
|
138
|
+
return add_node("in", label=f"x ∈ {set_to_str(v)}")
|
|
119
139
|
case IsInstancePredicate(klass):
|
|
120
140
|
name = klass[0].__name__ # type: ignore
|
|
121
141
|
return add_node("instance", label=f"is_{name}_p")
|
|
122
142
|
case IsNonePredicate():
|
|
123
143
|
return add_node("none", label="x = None")
|
|
144
|
+
case IsRealSubsetPredicate(v):
|
|
145
|
+
return add_node("real_subset", label=f"x ⊂ {set_to_str(v)}")
|
|
146
|
+
case IsSubsetPredicate(v):
|
|
147
|
+
return add_node("subset", label=f"x ⊆ {set_to_str(v)}")
|
|
148
|
+
case IsRealSupersetPredicate(v):
|
|
149
|
+
return add_node("real_superset", label=f"x ⊃ {set_to_str(v)}")
|
|
150
|
+
case IsSupersetPredicate(v):
|
|
151
|
+
return add_node("superset", label=f"x ⊇ {set_to_str(v)}")
|
|
124
152
|
case LazyPredicate(ref):
|
|
125
153
|
return add_node("lazy", label=ref)
|
|
126
154
|
case LePredicate(v):
|
|
@@ -130,35 +158,21 @@ def render(dot, predicate: Predicate, node_nr):
|
|
|
130
158
|
case NamedPredicate(name):
|
|
131
159
|
return add_node("named", label=name)
|
|
132
160
|
case NotInPredicate(v):
|
|
133
|
-
|
|
134
|
-
return add_node("in", label=f"x ∉ {{{items}}}")
|
|
161
|
+
return add_node("in", label=f"x ∉ {set_to_str(v)}")
|
|
135
162
|
case NePredicate(v):
|
|
136
163
|
return add_node("ne", label=f"x ≠ {v}")
|
|
137
164
|
case NotPredicate(not_predicate):
|
|
138
|
-
|
|
139
|
-
node = add_node("not", label="¬")
|
|
140
|
-
dot.edge(node, child)
|
|
141
|
-
return node
|
|
165
|
+
return add_node_with_child("not", label="¬", child=not_predicate)
|
|
142
166
|
case OrPredicate(left, right):
|
|
143
|
-
|
|
144
|
-
left_node = to_value(left)
|
|
145
|
-
right_node = to_value(right)
|
|
146
|
-
dot.edge(node, left_node)
|
|
147
|
-
dot.edge(node, right_node)
|
|
148
|
-
return node
|
|
149
|
-
case PredicateFactory() as factory:
|
|
150
|
-
return to_value(factory.predicate)
|
|
167
|
+
return add_node_left_right("or", label="∨", left=left, right=right)
|
|
151
168
|
case RootPredicate():
|
|
152
169
|
return add_node("root", label="root")
|
|
170
|
+
case TeePredicate():
|
|
171
|
+
return add_node("tee", label="tee")
|
|
153
172
|
case ThisPredicate():
|
|
154
173
|
return add_node("this", label="this")
|
|
155
174
|
case XorPredicate(left, right):
|
|
156
|
-
|
|
157
|
-
left_node = to_value(left)
|
|
158
|
-
right_node = to_value(right)
|
|
159
|
-
dot.edge(node, left_node)
|
|
160
|
-
dot.edge(node, right_node)
|
|
161
|
-
return node
|
|
175
|
+
return add_node_left_right("xor", label="⊻", left=left, right=right)
|
|
162
176
|
case _:
|
|
163
177
|
raise ValueError(f"Unknown predicate type {predicate}")
|
|
164
178
|
|
|
@@ -167,7 +181,7 @@ def render(dot, predicate: Predicate, node_nr):
|
|
|
167
181
|
render_lazy_references(dot, node_predicate_mapping)
|
|
168
182
|
|
|
169
183
|
|
|
170
|
-
def render_lazy_references(dot, node_predicate_mapping):
|
|
184
|
+
def render_lazy_references(dot, node_predicate_mapping) -> None:
|
|
171
185
|
def find_in_mapping(lookup: Predicate) -> str:
|
|
172
186
|
return first(node for node, predicate in node_predicate_mapping.items() if predicate == lookup)
|
|
173
187
|
|
|
@@ -190,14 +204,14 @@ def render_lazy_references(dot, node_predicate_mapping):
|
|
|
190
204
|
add_dashed_line(node, this)
|
|
191
205
|
|
|
192
206
|
|
|
193
|
-
def render_original(dot, predicate: Predicate, node_nr):
|
|
207
|
+
def render_original(dot, predicate: Predicate, node_nr) -> None:
|
|
194
208
|
with dot.subgraph(name="cluster_original") as original:
|
|
195
209
|
original.attr(style="filled", color="lightgrey")
|
|
196
210
|
original.attr(label="Original predicate")
|
|
197
211
|
render(original, predicate, node_nr)
|
|
198
212
|
|
|
199
213
|
|
|
200
|
-
def render_optimized(dot, predicate: Predicate, node_nr):
|
|
214
|
+
def render_optimized(dot, predicate: Predicate, node_nr) -> None:
|
|
201
215
|
optimized_predicate = optimize(predicate)
|
|
202
216
|
|
|
203
217
|
with dot.subgraph(name="cluster_optimized") as optimized:
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
+
from predicate.all_predicate import AllPredicate
|
|
4
|
+
from predicate.any_predicate import AnyPredicate
|
|
5
|
+
from predicate.named_predicate import NamedPredicate
|
|
3
6
|
from predicate.predicate import (
|
|
4
|
-
AllPredicate,
|
|
5
7
|
AlwaysFalsePredicate,
|
|
6
8
|
AlwaysTruePredicate,
|
|
7
9
|
AndPredicate,
|
|
8
|
-
AnyPredicate,
|
|
9
10
|
FnPredicate,
|
|
10
11
|
IsFalsyPredicate,
|
|
11
12
|
IsTruthyPredicate,
|
|
12
|
-
NamedPredicate,
|
|
13
13
|
NePredicate,
|
|
14
14
|
NotPredicate,
|
|
15
15
|
OrPredicate,
|
|
16
16
|
Predicate,
|
|
17
17
|
XorPredicate,
|
|
18
18
|
)
|
|
19
|
+
from predicate.tee_predicate import TeePredicate
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
def to_json(predicate: Predicate) -> dict[str, Any]:
|
|
@@ -48,6 +49,8 @@ def to_json(predicate: Predicate) -> dict[str, Any]:
|
|
|
48
49
|
return "not", {"predicate": to_json(not_predicate)}
|
|
49
50
|
case OrPredicate(left, right):
|
|
50
51
|
return "or", {"left": to_json(left), "right": to_json(right)}
|
|
52
|
+
case TeePredicate():
|
|
53
|
+
return "tee", None
|
|
51
54
|
case XorPredicate(left, right):
|
|
52
55
|
return "xor", {"left": to_json(left), "right": to_json(right)}
|
|
53
56
|
case _:
|