py-predicate 1.3__tar.gz → 1.4__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-1.3 → py_predicate-1.4}/PKG-INFO +2 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/__init__.py +31 -4
- {py_predicate-1.3 → py_predicate-1.4}/predicate/all_predicate.py +9 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/always_false_predicate.py +5 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/always_true_predicate.py +5 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/any_predicate.py +14 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/constructor/mutate.py +1 -1
- py_predicate-1.4/predicate/consumes.py +13 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/count_predicate.py +1 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/eq_predicate.py +5 -1
- py_predicate-1.4/predicate/exactly_predicate.py +48 -0
- py_predicate-1.4/predicate/exception_predicate.py +25 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/generator/generate_false.py +15 -6
- {py_predicate-1.3 → py_predicate-1.4}/predicate/generator/generate_true.py +11 -5
- {py_predicate-1.3 → py_predicate-1.4}/predicate/has_length_predicate.py +9 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/has_path_predicate.py +7 -3
- {py_predicate-1.3 → py_predicate-1.4}/predicate/helpers.py +1 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_instance_predicate.py +8 -8
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_none_predicate.py +10 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_not_none_predicate.py +5 -1
- py_predicate-1.4/predicate/is_predicate.py +30 -0
- py_predicate-1.4/predicate/is_same_predicate.py +44 -0
- py_predicate-1.4/predicate/match_predicate.py +74 -0
- py_predicate-1.4/predicate/mutual_recur_predicate.py +42 -0
- py_predicate-1.4/predicate/named_predicate.py +45 -0
- py_predicate-1.4/predicate/optional_predicate.py +45 -0
- py_predicate-1.4/predicate/plus_predicate.py +39 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/predicate.py +30 -2
- py_predicate-1.4/predicate/predicate_factory.py +21 -0
- py_predicate-1.4/predicate/recur_predicate.py +51 -0
- py_predicate-1.4/predicate/reduce_predicate.py +25 -0
- py_predicate-1.4/predicate/repeat_predicate.py +34 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/root_predicate.py +9 -11
- {py_predicate-1.3 → py_predicate-1.4}/predicate/spec/exercise.py +2 -5
- {py_predicate-1.3 → py_predicate-1.4}/predicate/standard_predicates.py +2 -19
- py_predicate-1.4/predicate/star_predicate.py +43 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/str_predicates.py +1 -1
- {py_predicate-1.3 → py_predicate-1.4}/predicate/tee_predicate.py +5 -1
- py_predicate-1.4/predicate/this_predicate.py +41 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/truth_table.py +1 -1
- {py_predicate-1.3 → py_predicate-1.4}/pyproject.toml +2 -1
- py_predicate-1.3/predicate/match_predicate.py +0 -230
- py_predicate-1.3/predicate/named_predicate.py +0 -17
- py_predicate-1.3/predicate/this_predicate.py +0 -33
- {py_predicate-1.3 → py_predicate-1.4}/LICENSE +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/README.md +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/comp_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/constructor/__init__.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/constructor/construct.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/constructor/helpers.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/dict_of_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/explain.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/fn_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/formatter/__init__.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/formatter/format_dot.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/formatter/format_json.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/formatter/format_latex.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/formatter/helpers.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/ge_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/generator/__init__.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/generator/generate_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/generator/helpers.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/gt_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/has_key_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/implies.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/implies_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/in_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/in_predicate_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/ip_address_predicates.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_callable_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_falsy_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_lambda_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_predicate_of_p.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_subclass_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/is_truthy_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/lazy_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/le_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/list_of_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/lt_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/ne_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/negate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/not_in_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/__init__.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/all_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/and_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/any_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/helpers.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/in_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/not_in_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/not_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/or_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/predicate_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/optimizer/xor_optimizer.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/parser.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/property_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/range_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/regex_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/set_of_predicate.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/set_predicates.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/spec/__init__.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/spec/instrument.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/spec/spec.py +0 -0
- {py_predicate-1.3 → py_predicate-1.4}/predicate/tuple_of_predicate.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py_predicate
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4
|
|
4
4
|
Summary: Module to create composable predicates
|
|
5
5
|
Author-email: Maurits Rijk <maurits.rijk@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -19,6 +19,7 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
19
19
|
Classifier: Environment :: Web Environment
|
|
20
20
|
Classifier: License :: OSI Approved :: MIT License
|
|
21
21
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
23
|
Classifier: Programming Language :: Python :: 3.13
|
|
23
24
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
25
|
License-File: LICENSE
|
|
@@ -7,9 +7,11 @@ from predicate.always_false_predicate import always_false_p, never_p
|
|
|
7
7
|
from predicate.always_true_predicate import always_p, always_true_p
|
|
8
8
|
from predicate.any_predicate import any_p
|
|
9
9
|
from predicate.comp_predicate import comp_p
|
|
10
|
-
from predicate.count_predicate import count_p
|
|
10
|
+
from predicate.count_predicate import count_p, exactly_one_p, exactly_zero_p
|
|
11
11
|
from predicate.dict_of_predicate import is_dict_of_p
|
|
12
12
|
from predicate.eq_predicate import eq_false_p, eq_p, eq_true_p, zero_p
|
|
13
|
+
from predicate.exactly_predicate import exactly_n
|
|
14
|
+
from predicate.exception_predicate import PredicateError, exception_p
|
|
13
15
|
from predicate.explain import explain
|
|
14
16
|
from predicate.fn_predicate import fn_p, is_even_p, is_finite_p, is_inf_p, is_nan_p, is_odd_p
|
|
15
17
|
from predicate.formatter import to_dot, to_json, to_latex
|
|
@@ -39,21 +41,30 @@ from predicate.is_instance_predicate import (
|
|
|
39
41
|
is_uuid_p,
|
|
40
42
|
)
|
|
41
43
|
from predicate.is_lambda_predicate import is_lambda_p, is_lambda_with_signature_p
|
|
42
|
-
from predicate.is_none_predicate import is_none_p
|
|
43
|
-
from predicate.is_not_none_predicate import is_not_none_p
|
|
44
|
+
from predicate.is_none_predicate import is_none_p, none_is_exception_p, none_is_true_p
|
|
45
|
+
from predicate.is_not_none_predicate import is_not_none_p, none_is_false_p
|
|
46
|
+
from predicate.is_predicate import is_p
|
|
44
47
|
from predicate.is_predicate_of_p import is_predicate_of_p
|
|
48
|
+
from predicate.is_same_predicate import is_same_p
|
|
45
49
|
from predicate.is_subclass_predicate import is_enum_p, is_int_enum_p, is_str_enum_p, is_subclass_p
|
|
46
50
|
from predicate.is_truthy_predicate import is_truthy_p
|
|
47
51
|
from predicate.lazy_predicate import lazy_p
|
|
48
52
|
from predicate.le_predicate import le_p
|
|
49
53
|
from predicate.list_of_predicate import is_list_of_p
|
|
50
54
|
from predicate.lt_predicate import lt_p, neg_p
|
|
51
|
-
from predicate.match_predicate import
|
|
55
|
+
from predicate.match_predicate import match_p
|
|
56
|
+
from predicate.mutual_recur_predicate import mutual_recur_p
|
|
52
57
|
from predicate.ne_predicate import ne_p
|
|
53
58
|
from predicate.not_in_predicate import not_in_p
|
|
54
59
|
from predicate.optimizer.predicate_optimizer import can_optimize, optimize
|
|
60
|
+
from predicate.optional_predicate import optional
|
|
61
|
+
from predicate.plus_predicate import plus
|
|
62
|
+
from predicate.predicate import and_p, or_p, xor_p
|
|
55
63
|
from predicate.range_predicate import ge_le_p, ge_lt_p, gt_le_p, gt_lt_p
|
|
64
|
+
from predicate.recur_predicate import recur_p
|
|
65
|
+
from predicate.reduce_predicate import reduce_p
|
|
56
66
|
from predicate.regex_predicate import regex_p
|
|
67
|
+
from predicate.repeat_predicate import repeat
|
|
57
68
|
from predicate.set_of_predicate import is_set_of_p
|
|
58
69
|
from predicate.set_predicates import (
|
|
59
70
|
is_real_subset_p,
|
|
@@ -74,15 +85,18 @@ from predicate.standard_predicates import (
|
|
|
74
85
|
root_p,
|
|
75
86
|
this_p,
|
|
76
87
|
)
|
|
88
|
+
from predicate.star_predicate import star
|
|
77
89
|
from predicate.tee_predicate import tee_p
|
|
78
90
|
from predicate.tuple_of_predicate import is_tuple_of_p
|
|
79
91
|
|
|
80
92
|
__all__ = [
|
|
93
|
+
"PredicateError",
|
|
81
94
|
"Spec",
|
|
82
95
|
"all_p",
|
|
83
96
|
"always_false_p",
|
|
84
97
|
"always_p",
|
|
85
98
|
"always_true_p",
|
|
99
|
+
"and_p",
|
|
86
100
|
"any_p",
|
|
87
101
|
"can_optimize",
|
|
88
102
|
"comp_p",
|
|
@@ -91,6 +105,9 @@ __all__ = [
|
|
|
91
105
|
"eq_p",
|
|
92
106
|
"eq_true_p",
|
|
93
107
|
"exactly_n",
|
|
108
|
+
"exactly_one_p",
|
|
109
|
+
"exactly_zero_p",
|
|
110
|
+
"exception_p",
|
|
94
111
|
"exercise",
|
|
95
112
|
"explain",
|
|
96
113
|
"fn_p",
|
|
@@ -138,11 +155,13 @@ __all__ = [
|
|
|
138
155
|
"is_list_of_p",
|
|
139
156
|
"is_list_p",
|
|
140
157
|
"is_lower_p",
|
|
158
|
+
"is_same_p",
|
|
141
159
|
"is_nan_p",
|
|
142
160
|
"is_none_p",
|
|
143
161
|
"is_not_empty_p",
|
|
144
162
|
"is_not_none_p",
|
|
145
163
|
"is_odd_p",
|
|
164
|
+
"is_p",
|
|
146
165
|
"is_predicate_of_p",
|
|
147
166
|
"is_predicate_p",
|
|
148
167
|
"is_range_p",
|
|
@@ -165,14 +184,21 @@ __all__ = [
|
|
|
165
184
|
"le_p",
|
|
166
185
|
"lt_p",
|
|
167
186
|
"match_p",
|
|
187
|
+
"mutual_recur_p",
|
|
168
188
|
"ne_p",
|
|
169
189
|
"neg_p",
|
|
170
190
|
"never_p",
|
|
191
|
+
"none_is_exception_p",
|
|
192
|
+
"none_is_false_p",
|
|
193
|
+
"none_is_true_p",
|
|
171
194
|
"not_in_p",
|
|
172
195
|
"optimize",
|
|
173
196
|
"optional",
|
|
197
|
+
"or_p",
|
|
174
198
|
"plus",
|
|
175
199
|
"pos_p",
|
|
200
|
+
"recur_p",
|
|
201
|
+
"reduce_p",
|
|
176
202
|
"regex_p",
|
|
177
203
|
"repeat",
|
|
178
204
|
"root_p",
|
|
@@ -182,6 +208,7 @@ __all__ = [
|
|
|
182
208
|
"to_dot",
|
|
183
209
|
"to_json",
|
|
184
210
|
"to_latex",
|
|
211
|
+
"xor_p",
|
|
185
212
|
"zero_p",
|
|
186
213
|
]
|
|
187
214
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from
|
|
2
|
+
from itertools import takewhile
|
|
3
|
+
from typing import Any, Iterable, Iterator, override
|
|
4
|
+
|
|
5
|
+
from more_itertools import ilen
|
|
3
6
|
|
|
4
7
|
from predicate.helpers import first_false
|
|
5
8
|
from predicate.predicate import Predicate, resolve_predicate
|
|
@@ -35,6 +38,11 @@ class AllPredicate[T](Predicate[T]):
|
|
|
35
38
|
|
|
36
39
|
return {"reason": f"Item '{fail}' didn't match predicate {self.predicate!r}"}
|
|
37
40
|
|
|
41
|
+
@override
|
|
42
|
+
def consumes(self, iterable: Iterable[Any]) -> Iterator[int]:
|
|
43
|
+
consumed = takewhile(self.predicate, iterable)
|
|
44
|
+
yield from range(0, ilen(consumed) + 1)
|
|
45
|
+
|
|
38
46
|
|
|
39
47
|
def all_p[T](predicate: Predicate[T]) -> AllPredicate[T]:
|
|
40
48
|
"""Return True if the predicate holds for each item in the iterable, otherwise False."""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Any, Final, override
|
|
2
|
+
from typing import Any, Final, Iterable, Iterator, override
|
|
3
3
|
|
|
4
4
|
from predicate.predicate import Predicate
|
|
5
5
|
|
|
@@ -18,6 +18,10 @@ class AlwaysFalsePredicate(Predicate):
|
|
|
18
18
|
def explain_failure(self, *args, **kwargs) -> dict:
|
|
19
19
|
return {"reason": "Always returns False"}
|
|
20
20
|
|
|
21
|
+
@override
|
|
22
|
+
def consumes(self, iterable: Iterable[Any]) -> Iterator[int]:
|
|
23
|
+
yield 0
|
|
24
|
+
|
|
21
25
|
@override
|
|
22
26
|
def get_klass(self) -> type:
|
|
23
27
|
return type(Any)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Any, Final, override
|
|
2
|
+
from typing import Any, Final, Iterable, Iterator, override
|
|
3
3
|
|
|
4
4
|
from predicate.predicate import Predicate
|
|
5
5
|
|
|
@@ -14,6 +14,10 @@ class AlwaysTruePredicate(Predicate):
|
|
|
14
14
|
def __repr__(self) -> str:
|
|
15
15
|
return "always_true_p"
|
|
16
16
|
|
|
17
|
+
@override
|
|
18
|
+
def consumes(self, iterable: Iterable[Any]) -> Iterator[int]:
|
|
19
|
+
yield 1
|
|
20
|
+
|
|
17
21
|
@override
|
|
18
22
|
def get_klass(self) -> type:
|
|
19
23
|
return type(Any)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from
|
|
2
|
+
from itertools import takewhile
|
|
3
|
+
from typing import Any, Iterable, Iterator, override
|
|
4
|
+
|
|
5
|
+
from more_itertools import ilen
|
|
3
6
|
|
|
4
7
|
from predicate.predicate import Predicate, resolve_predicate
|
|
5
8
|
|
|
@@ -32,6 +35,16 @@ class AnyPredicate[T](Predicate[T]):
|
|
|
32
35
|
def explain_failure(self, iterable: Iterable[T]) -> dict:
|
|
33
36
|
return {"reason": f"No item matches predicate {self.predicate!r}"}
|
|
34
37
|
|
|
38
|
+
@override
|
|
39
|
+
def consumes(self, iterable: Iterable[Any]) -> Iterator[int]:
|
|
40
|
+
consumed = takewhile(~self.predicate, iterable)
|
|
41
|
+
len_consumed = ilen(consumed)
|
|
42
|
+
len_iterable = ilen(iterable)
|
|
43
|
+
if len_consumed < len_iterable:
|
|
44
|
+
yield from range(len_consumed, len_iterable + 1)
|
|
45
|
+
else:
|
|
46
|
+
yield 0
|
|
47
|
+
|
|
35
48
|
|
|
36
49
|
def any_p[T](predicate: Predicate[T]) -> AnyPredicate[T]:
|
|
37
50
|
"""Return True if the predicate holds for any item in the iterable, otherwise False."""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Any, Iterable, Iterator
|
|
2
|
+
|
|
3
|
+
from more_itertools import spy
|
|
4
|
+
|
|
5
|
+
from predicate.predicate import Predicate
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def consumes(predicate: Predicate, iterable: Iterable[Any]) -> Iterator[int]:
|
|
9
|
+
head, _ = spy(iterable, 1)
|
|
10
|
+
if head:
|
|
11
|
+
yield from predicate.consumes(iterable)
|
|
12
|
+
else:
|
|
13
|
+
yield 0
|
|
@@ -10,7 +10,7 @@ from predicate.predicate import Predicate
|
|
|
10
10
|
|
|
11
11
|
@dataclass
|
|
12
12
|
class CountPredicate[T](Predicate[T]):
|
|
13
|
-
"""A predicate class that models the '
|
|
13
|
+
"""A predicate class that models the 'count' predicate."""
|
|
14
14
|
|
|
15
15
|
predicate: Predicate[T]
|
|
16
16
|
length_p: Predicate[int]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Final, override
|
|
2
|
+
from typing import Any, Final, Iterable, Iterator, override
|
|
3
3
|
|
|
4
4
|
from predicate.predicate import Predicate
|
|
5
5
|
|
|
@@ -24,6 +24,10 @@ class EqPredicate[T](Predicate[T]):
|
|
|
24
24
|
def explain_failure(self, x: T) -> dict:
|
|
25
25
|
return {"reason": f"{x} is not equal to {self.v!r}"}
|
|
26
26
|
|
|
27
|
+
@override
|
|
28
|
+
def consumes(self, iterable: Iterable[Any]) -> Iterator[int]:
|
|
29
|
+
yield from self.consumes_single(iterable)
|
|
30
|
+
|
|
27
31
|
|
|
28
32
|
def eq_p[T](v: T) -> EqPredicate[T]:
|
|
29
33
|
"""Return True if the value is equal to the constant, otherwise False."""
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Iterable, override
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class ExactlyPredicate[T](Predicate[T]):
|
|
9
|
+
"""Match exactly n instances of the given predicate."""
|
|
10
|
+
|
|
11
|
+
n: int
|
|
12
|
+
predicate: Predicate
|
|
13
|
+
|
|
14
|
+
def __call__(self, iterable: Iterable, *, predicates: list[Predicate], full_match: bool) -> bool:
|
|
15
|
+
from predicate.match_predicate import match
|
|
16
|
+
|
|
17
|
+
rest = iterable
|
|
18
|
+
for _ in range(self.n):
|
|
19
|
+
if not rest:
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
item, *rest = rest
|
|
23
|
+
if not self.predicate(item):
|
|
24
|
+
return False
|
|
25
|
+
return match(rest, predicates=predicates, full_match=full_match) if predicates else True
|
|
26
|
+
|
|
27
|
+
def __repr__(self) -> str:
|
|
28
|
+
return f"exactly({self.n}, {self.predicate!r})"
|
|
29
|
+
|
|
30
|
+
@override
|
|
31
|
+
def explain_failure(self, iterable: Iterable[T], *, predicates: list[Predicate], full_match: bool) -> dict: # type: ignore
|
|
32
|
+
from predicate.match_predicate import reason
|
|
33
|
+
|
|
34
|
+
rest = iterable
|
|
35
|
+
for _ in range(self.n):
|
|
36
|
+
if not rest:
|
|
37
|
+
return {"reason": f"Not enough items in iterable, expected {self.n}"}
|
|
38
|
+
|
|
39
|
+
item, *rest = rest
|
|
40
|
+
if not self.predicate(item):
|
|
41
|
+
return self.predicate.explain(item)
|
|
42
|
+
|
|
43
|
+
return reason(rest, predicates=predicates, full_match=full_match)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def exactly_n(n: int, predicate: Predicate) -> Predicate:
|
|
47
|
+
"""Match exactly n instances of the given predicate."""
|
|
48
|
+
return ExactlyPredicate(n=n, predicate=predicate)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Final
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PredicateError(Exception):
|
|
8
|
+
"""Exception that will be thrown."""
|
|
9
|
+
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class ExceptionPredicate[T](Predicate[T]):
|
|
15
|
+
"""A predicate class always throws an exception."""
|
|
16
|
+
|
|
17
|
+
def __call__(self, _x: T) -> bool:
|
|
18
|
+
raise PredicateError()
|
|
19
|
+
|
|
20
|
+
def __repr__(self) -> str:
|
|
21
|
+
return "exception_p"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
exception_p: Final[ExceptionPredicate] = ExceptionPredicate()
|
|
25
|
+
"""Always throw an exception."""
|
|
@@ -3,7 +3,7 @@ import sys
|
|
|
3
3
|
from collections.abc import Iterable, Iterator
|
|
4
4
|
from datetime import datetime, timedelta
|
|
5
5
|
from functools import singledispatch
|
|
6
|
-
from itertools import repeat
|
|
6
|
+
from itertools import cycle, repeat
|
|
7
7
|
from types import UnionType
|
|
8
8
|
from typing import Final, get_args
|
|
9
9
|
from uuid import UUID
|
|
@@ -17,6 +17,7 @@ from predicate.any_predicate import AnyPredicate
|
|
|
17
17
|
from predicate.count_predicate import CountPredicate
|
|
18
18
|
from predicate.dict_of_predicate import DictOfPredicate, is_dict_of_p
|
|
19
19
|
from predicate.eq_predicate import EqPredicate
|
|
20
|
+
from predicate.exactly_predicate import ExactlyPredicate
|
|
20
21
|
from predicate.fn_predicate import FnPredicate
|
|
21
22
|
from predicate.ge_predicate import GePredicate
|
|
22
23
|
from predicate.generator.helpers import (
|
|
@@ -45,26 +46,26 @@ from predicate.is_instance_predicate import IsInstancePredicate
|
|
|
45
46
|
from predicate.is_lambda_predicate import IsLambdaPredicate
|
|
46
47
|
from predicate.is_none_predicate import IsNonePredicate
|
|
47
48
|
from predicate.is_not_none_predicate import IsNotNonePredicate
|
|
49
|
+
from predicate.is_predicate import IsPredicate
|
|
48
50
|
from predicate.is_subclass_predicate import IsSubclassPredicate
|
|
49
51
|
from predicate.is_truthy_predicate import IsTruthyPredicate
|
|
50
52
|
from predicate.le_predicate import LePredicate
|
|
51
53
|
from predicate.list_of_predicate import ListOfPredicate, is_list_of_p
|
|
52
54
|
from predicate.lt_predicate import LtPredicate
|
|
53
55
|
from predicate.match_predicate import (
|
|
54
|
-
ExactlyPredicate,
|
|
55
56
|
MatchPredicate,
|
|
56
|
-
OptionalPredicate,
|
|
57
|
-
PlusPredicate,
|
|
58
|
-
RepeatPredicate,
|
|
59
|
-
StarPredicate,
|
|
60
57
|
)
|
|
61
58
|
from predicate.ne_predicate import NePredicate, ne_p
|
|
62
59
|
from predicate.not_in_predicate import NotInPredicate
|
|
63
60
|
from predicate.optimizer.predicate_optimizer import optimize
|
|
61
|
+
from predicate.optional_predicate import OptionalPredicate
|
|
62
|
+
from predicate.plus_predicate import PlusPredicate
|
|
64
63
|
from predicate.predicate import AndPredicate, NotPredicate, OrPredicate, Predicate, XorPredicate
|
|
65
64
|
from predicate.range_predicate import GeLePredicate, GeLtPredicate, GtLePredicate, GtLtPredicate, ge_le_p
|
|
65
|
+
from predicate.repeat_predicate import RepeatPredicate
|
|
66
66
|
from predicate.set_of_predicate import SetOfPredicate
|
|
67
67
|
from predicate.standard_predicates import is_int_p
|
|
68
|
+
from predicate.star_predicate import StarPredicate
|
|
68
69
|
from predicate.tee_predicate import TeePredicate
|
|
69
70
|
from predicate.tuple_of_predicate import TupleOfPredicate
|
|
70
71
|
|
|
@@ -547,3 +548,11 @@ def generate_is_subclass(is_subclass_predicate: IsSubclassPredicate) -> Iterator
|
|
|
547
548
|
if non_subclasses := all_sub_classes - subclasses:
|
|
548
549
|
while True:
|
|
549
550
|
yield from non_subclasses
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
@generate_false.register
|
|
554
|
+
def generate_is(is_predicate: IsPredicate) -> Iterator:
|
|
555
|
+
singletons = (True, False, None, Ellipsis, (), NotImplemented)
|
|
556
|
+
selection = [v for v in singletons if v is not is_predicate.v]
|
|
557
|
+
|
|
558
|
+
yield from cycle(selection)
|
|
@@ -26,6 +26,7 @@ from predicate.any_predicate import AnyPredicate
|
|
|
26
26
|
from predicate.count_predicate import CountPredicate
|
|
27
27
|
from predicate.dict_of_predicate import DictOfPredicate, is_dict_of_p
|
|
28
28
|
from predicate.eq_predicate import EqPredicate, eq_p
|
|
29
|
+
from predicate.exactly_predicate import ExactlyPredicate
|
|
29
30
|
from predicate.fn_predicate import FnPredicate
|
|
30
31
|
from predicate.ge_predicate import GePredicate, ge_p
|
|
31
32
|
from predicate.generator.helpers import (
|
|
@@ -66,6 +67,7 @@ from predicate.is_instance_predicate import IsInstancePredicate
|
|
|
66
67
|
from predicate.is_lambda_predicate import IsLambdaPredicate
|
|
67
68
|
from predicate.is_none_predicate import IsNonePredicate
|
|
68
69
|
from predicate.is_not_none_predicate import IsNotNonePredicate
|
|
70
|
+
from predicate.is_predicate import IsPredicate
|
|
69
71
|
from predicate.is_predicate_of_p import IsPredicateOfPredicate
|
|
70
72
|
from predicate.is_subclass_predicate import IsSubclassPredicate
|
|
71
73
|
from predicate.is_truthy_predicate import IsTruthyPredicate
|
|
@@ -73,16 +75,13 @@ from predicate.le_predicate import LePredicate
|
|
|
73
75
|
from predicate.list_of_predicate import ListOfPredicate, is_list_of_p
|
|
74
76
|
from predicate.lt_predicate import LtPredicate
|
|
75
77
|
from predicate.match_predicate import (
|
|
76
|
-
ExactlyPredicate,
|
|
77
78
|
MatchPredicate,
|
|
78
|
-
OptionalPredicate,
|
|
79
|
-
PlusPredicate,
|
|
80
|
-
RepeatPredicate,
|
|
81
|
-
StarPredicate,
|
|
82
79
|
)
|
|
83
80
|
from predicate.ne_predicate import NePredicate
|
|
84
81
|
from predicate.not_in_predicate import NotInPredicate
|
|
85
82
|
from predicate.optimizer.predicate_optimizer import optimize
|
|
83
|
+
from predicate.optional_predicate import OptionalPredicate
|
|
84
|
+
from predicate.plus_predicate import PlusPredicate
|
|
86
85
|
from predicate.predicate import (
|
|
87
86
|
AndPredicate,
|
|
88
87
|
ConstrainedT,
|
|
@@ -94,12 +93,14 @@ from predicate.predicate import (
|
|
|
94
93
|
from predicate.property_predicate import PropertyPredicate
|
|
95
94
|
from predicate.range_predicate import GeLePredicate, GeLtPredicate, GtLePredicate, GtLtPredicate, ge_le_p
|
|
96
95
|
from predicate.regex_predicate import RegexPredicate
|
|
96
|
+
from predicate.repeat_predicate import RepeatPredicate
|
|
97
97
|
from predicate.set_of_predicate import SetOfPredicate
|
|
98
98
|
from predicate.set_predicates import IsRealSubsetPredicate, IsSubsetPredicate
|
|
99
99
|
from predicate.standard_predicates import (
|
|
100
100
|
is_int_p,
|
|
101
101
|
is_list_p,
|
|
102
102
|
)
|
|
103
|
+
from predicate.star_predicate import StarPredicate
|
|
103
104
|
from predicate.tee_predicate import TeePredicate
|
|
104
105
|
from predicate.tuple_of_predicate import TupleOfPredicate
|
|
105
106
|
|
|
@@ -666,3 +667,8 @@ def generate_is_subclass(is_subclass_predicate: IsSubclassPredicate) -> Iterator
|
|
|
666
667
|
subclasses = klass.__subclasses__()
|
|
667
668
|
while True:
|
|
668
669
|
yield from subclasses
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
@generate_true.register
|
|
673
|
+
def generate_is(is_predicate: IsPredicate) -> Iterator:
|
|
674
|
+
yield from repeat(is_predicate.v)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from
|
|
2
|
+
from itertools import islice
|
|
3
|
+
from typing import Any, Final, Iterable, Iterator, override
|
|
3
4
|
|
|
4
5
|
from more_itertools import ilen
|
|
5
6
|
|
|
@@ -24,6 +25,13 @@ class HasLengthPredicate[T](Predicate[T]):
|
|
|
24
25
|
def explain_failure(self, iterable: Iterable[T]) -> dict:
|
|
25
26
|
return {"reason": f"Expected length {self.length_p!r}, actual: {ilen(iterable)}"}
|
|
26
27
|
|
|
28
|
+
@override
|
|
29
|
+
def consumes(self, iterable: Iterable[Any]) -> Iterator[int]:
|
|
30
|
+
for end in range(0, ilen(iterable) + 1):
|
|
31
|
+
rest = islice(iterable, 0, end)
|
|
32
|
+
if self(rest):
|
|
33
|
+
yield end
|
|
34
|
+
|
|
27
35
|
|
|
28
36
|
def has_length_p(length_p: Predicate[int]) -> Predicate[Iterable]:
|
|
29
37
|
"""Return True if length of iterable is equal to value, otherwise False."""
|
|
@@ -14,7 +14,7 @@ class HasPathPredicate[T](Predicate[T]):
|
|
|
14
14
|
def __call__(self, x: Any) -> bool:
|
|
15
15
|
match x:
|
|
16
16
|
case dict() as d:
|
|
17
|
-
return match_dict(self.path
|
|
17
|
+
return match_dict(d, path=self.path)
|
|
18
18
|
case _:
|
|
19
19
|
return False
|
|
20
20
|
|
|
@@ -31,7 +31,11 @@ class HasPathPredicate[T](Predicate[T]):
|
|
|
31
31
|
return {"reason": f"Value {x} is not a dict"}
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
def match_dict(
|
|
34
|
+
def match_dict(
|
|
35
|
+
x: dict,
|
|
36
|
+
*,
|
|
37
|
+
path: list[Predicate],
|
|
38
|
+
) -> bool:
|
|
35
39
|
first_p, *rest = path
|
|
36
40
|
found = [v for k, v in x.items() if first_p(k)]
|
|
37
41
|
return any(match_rest(value, rest) for value in found)
|
|
@@ -40,7 +44,7 @@ def match_dict(path: list[Predicate], x: dict) -> bool:
|
|
|
40
44
|
def match_rest(value: Any, rest_path: list[Predicate]) -> bool:
|
|
41
45
|
match value:
|
|
42
46
|
case dict() as d if rest_path:
|
|
43
|
-
return match_dict(
|
|
47
|
+
return match_dict(d, path=rest_path)
|
|
44
48
|
case list() as l if rest_path:
|
|
45
49
|
first_p, *rest = rest_path
|
|
46
50
|
return first_p(l) and any(match_rest(value, rest) for value in l)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from collections.abc import Callable, Container, Iterable
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import Hashable, Iterator, get_origin, override
|
|
4
|
+
from typing import Any, Hashable, Iterator, get_origin, override
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
7
|
from predicate.helpers import join_with_or
|
|
@@ -12,7 +12,7 @@ from predicate.predicate import Predicate
|
|
|
12
12
|
class IsInstancePredicate[T](Predicate[T]):
|
|
13
13
|
"""A predicate class that models the 'isinstance' predicate."""
|
|
14
14
|
|
|
15
|
-
instance_klass:
|
|
15
|
+
instance_klass: tuple
|
|
16
16
|
|
|
17
17
|
def __call__(self, x: object) -> bool:
|
|
18
18
|
# This is different from standard Python behaviour: a False/True value is not an int!
|
|
@@ -33,17 +33,17 @@ class IsInstancePredicate[T](Predicate[T]):
|
|
|
33
33
|
@override
|
|
34
34
|
def explain_failure(self, x: T) -> dict:
|
|
35
35
|
def class_names() -> Iterator[str]:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
for klass in klasses:
|
|
39
|
-
yield klass.__name__
|
|
40
|
-
case _:
|
|
41
|
-
yield self.instance_klass.__name__
|
|
36
|
+
for klass in self.instance_klass:
|
|
37
|
+
yield klass.__name__
|
|
42
38
|
|
|
43
39
|
klasses = join_with_or(list(class_names()))
|
|
44
40
|
|
|
45
41
|
return {"reason": f"{x} is not an instance of type {klasses}"}
|
|
46
42
|
|
|
43
|
+
@override
|
|
44
|
+
def consumes(self, iterable: Iterable[Any]) -> Iterator[int]:
|
|
45
|
+
yield from self.consumes_single(iterable)
|
|
46
|
+
|
|
47
47
|
|
|
48
48
|
def is_instance_p(*klass: type) -> Predicate:
|
|
49
49
|
"""Return True if value is an instance of one of the classes, otherwise False."""
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Final, override
|
|
3
3
|
|
|
4
|
-
from predicate
|
|
4
|
+
from predicate import exception_p
|
|
5
|
+
from predicate.predicate import Predicate, or_p, predicate_partial
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
@dataclass
|
|
@@ -21,3 +22,11 @@ class IsNonePredicate[T](Predicate[T]):
|
|
|
21
22
|
|
|
22
23
|
is_none_p: Final[IsNonePredicate] = IsNonePredicate()
|
|
23
24
|
"""Return True if value is None, otherwise False."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
none_is_true_p = predicate_partial(or_p, is_none_p)
|
|
28
|
+
"""Return True if value is None, otherwise the result of the predicate."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
none_is_exception_p = predicate_partial(or_p, is_none_p & exception_p)
|
|
32
|
+
"""Raise an exception if value is None, otherwise returns the result of the predicate."""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Final, override
|
|
3
3
|
|
|
4
|
-
from predicate.predicate import Predicate
|
|
4
|
+
from predicate.predicate import Predicate, and_p, predicate_partial
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
@dataclass
|
|
@@ -21,3 +21,7 @@ class IsNotNonePredicate[T](Predicate[T]):
|
|
|
21
21
|
|
|
22
22
|
is_not_none_p: Final[IsNotNonePredicate] = IsNotNonePredicate()
|
|
23
23
|
"""Return True if value is not None, otherwise False."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
none_is_false_p = predicate_partial(and_p, is_not_none_p)
|
|
27
|
+
"""Return False if value is None, otherwise the result of the predicate."""
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import override
|
|
3
|
+
|
|
4
|
+
from predicate.predicate import Predicate
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class IsPredicate[T](Predicate[T]):
|
|
9
|
+
"""A predicate class that models the 'is' predicate."""
|
|
10
|
+
|
|
11
|
+
v: T
|
|
12
|
+
|
|
13
|
+
def __call__(self, x: T) -> bool:
|
|
14
|
+
return x is self.v
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f"is_p({self.v!r})"
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def get_klass(self) -> type:
|
|
21
|
+
return type(self.v)
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
def explain_failure(self, x: T) -> dict:
|
|
25
|
+
return {"reason": f"{x} does not refer to {self.v!r}"}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_p[T](v: T) -> IsPredicate[T]:
|
|
29
|
+
"""Return True if the value is equal to the constant, otherwise False."""
|
|
30
|
+
return IsPredicate(v=v)
|