py-predicate 0.4__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.
Files changed (49) hide show
  1. {py_predicate-0.4 → py_predicate-0.5}/PKG-INFO +3 -3
  2. {py_predicate-0.4 → py_predicate-0.5}/README.md +2 -2
  3. {py_predicate-0.4 → py_predicate-0.5}/predicate/__init__.py +44 -11
  4. {py_predicate-0.4 → py_predicate-0.5}/predicate/all_predicate.py +2 -5
  5. {py_predicate-0.4 → py_predicate-0.5}/predicate/any_predicate.py +2 -5
  6. {py_predicate-0.4 → py_predicate-0.5}/predicate/formatter/format_dot.py +51 -46
  7. {py_predicate-0.4 → py_predicate-0.5}/predicate/generator/generate_false.py +55 -3
  8. {py_predicate-0.4 → py_predicate-0.5}/predicate/generator/generate_true.py +50 -6
  9. {py_predicate-0.4 → py_predicate-0.5}/predicate/generator/helpers.py +8 -9
  10. {py_predicate-0.4 → py_predicate-0.5}/predicate/has_key_predicate.py +1 -5
  11. py_predicate-0.5/predicate/has_length_predicate.py +19 -0
  12. {py_predicate-0.4 → py_predicate-0.5}/predicate/implies.py +48 -3
  13. py_predicate-0.5/predicate/ip_address_predicates.py +47 -0
  14. {py_predicate-0.4 → py_predicate-0.5}/predicate/is_instance_predicate.py +0 -3
  15. {py_predicate-0.4 → py_predicate-0.5}/predicate/named_predicate.py +0 -3
  16. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/and_optimizer.py +5 -12
  17. py_predicate-0.5/predicate/optimizer/in_optimizer.py +24 -0
  18. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/or_optimizer.py +12 -9
  19. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/predicate_optimizer.py +6 -2
  20. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/xor_optimizer.py +7 -2
  21. {py_predicate-0.4 → py_predicate-0.5}/predicate/predicate.py +0 -76
  22. py_predicate-0.5/predicate/property_predicate.py +20 -0
  23. {py_predicate-0.4 → py_predicate-0.5}/predicate/range_predicate.py +0 -12
  24. py_predicate-0.5/predicate/set_of_predicate.py +16 -0
  25. py_predicate-0.5/predicate/set_predicates.py +120 -0
  26. {py_predicate-0.4 → py_predicate-0.5}/predicate/standard_predicates.py +8 -22
  27. py_predicate-0.5/predicate/str_predicates.py +55 -0
  28. py_predicate-0.5/predicate/tuple_of_predicate.py +19 -0
  29. {py_predicate-0.4 → py_predicate-0.5}/pyproject.toml +1 -1
  30. py_predicate-0.4/predicate/optimizer/in_optimizer.py +0 -13
  31. {py_predicate-0.4 → py_predicate-0.5}/predicate/comp_predicate.py +0 -0
  32. {py_predicate-0.4 → py_predicate-0.5}/predicate/constructor/__init__.py +0 -0
  33. {py_predicate-0.4 → py_predicate-0.5}/predicate/constructor/construct.py +0 -0
  34. {py_predicate-0.4 → py_predicate-0.5}/predicate/formatter/__init__.py +0 -0
  35. {py_predicate-0.4 → py_predicate-0.5}/predicate/formatter/format_json.py +0 -0
  36. {py_predicate-0.4 → py_predicate-0.5}/predicate/generator/__init__.py +0 -0
  37. {py_predicate-0.4 → py_predicate-0.5}/predicate/lazy_predicate.py +0 -0
  38. {py_predicate-0.4 → py_predicate-0.5}/predicate/negate.py +0 -0
  39. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/__init__.py +0 -0
  40. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/all_optimizer.py +0 -0
  41. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/any_optimizer.py +0 -0
  42. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/not_optimizer.py +0 -0
  43. {py_predicate-0.4 → py_predicate-0.5}/predicate/optimizer/rules.py +0 -0
  44. {py_predicate-0.4 → py_predicate-0.5}/predicate/parser.py +0 -0
  45. {py_predicate-0.4 → py_predicate-0.5}/predicate/regex_predicate.py +0 -0
  46. {py_predicate-0.4 → py_predicate-0.5}/predicate/root_predicate.py +0 -0
  47. {py_predicate-0.4 → py_predicate-0.5}/predicate/tee_predicate.py +0 -0
  48. {py_predicate-0.4 → py_predicate-0.5}/predicate/this_predicate.py +0 -0
  49. {py_predicate-0.4 → py_predicate-0.5}/predicate/truth_table.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: py_predicate
3
- Version: 0.4
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
@@ -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, lazy_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(lazy_p("str_or_list_of_str")))
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, lazy_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(lazy_p("str_or_list_of_str")))
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.
@@ -17,14 +17,12 @@ from predicate.predicate import (
17
17
  FnPredicate,
18
18
  GePredicate,
19
19
  GtPredicate,
20
- InPredicate,
21
20
  IsEmptyPredicate,
22
21
  IsNonePredicate,
23
22
  IsNotNonePredicate,
24
23
  LePredicate,
25
24
  LtPredicate,
26
25
  NePredicate,
27
- NotInPredicate,
28
26
  NotPredicate,
29
27
  OrPredicate,
30
28
  Predicate,
@@ -33,6 +31,16 @@ from predicate.predicate import (
33
31
  always_true_p,
34
32
  is_empty_p,
35
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
+ )
36
44
  from predicate.standard_predicates import (
37
45
  all_p,
38
46
  any_p,
@@ -47,7 +55,7 @@ from predicate.standard_predicates import (
47
55
  gt_le_p,
48
56
  gt_lt_p,
49
57
  gt_p,
50
- in_p,
58
+ has_length_p,
51
59
  is_bool_p,
52
60
  is_callable_p,
53
61
  is_complex_p,
@@ -68,6 +76,7 @@ from predicate.standard_predicates import (
68
76
  is_none_p,
69
77
  is_not_none_p,
70
78
  is_predicate_p,
79
+ is_range_p,
71
80
  is_set_of_p,
72
81
  is_set_p,
73
82
  is_str_p,
@@ -80,7 +89,6 @@ from predicate.standard_predicates import (
80
89
  lt_p,
81
90
  ne_p,
82
91
  neg_p,
83
- not_in_p,
84
92
  pos_p,
85
93
  regex_p,
86
94
  root_p,
@@ -90,9 +98,9 @@ from predicate.standard_predicates import (
90
98
  )
91
99
 
92
100
  __all__ = [
101
+ "AllPredicate",
93
102
  "AlwaysFalsePredicate",
94
103
  "AlwaysTruePredicate",
95
- "AllPredicate",
96
104
  "AndPredicate",
97
105
  "AnyPredicate",
98
106
  "EqPredicate",
@@ -120,7 +128,6 @@ __all__ = [
120
128
  "eq_false_p",
121
129
  "eq_p",
122
130
  "eq_true_p",
123
- "is_falsy_p",
124
131
  "fn_p",
125
132
  "ge_le_p",
126
133
  "ge_lt_p",
@@ -130,33 +137,48 @@ __all__ = [
130
137
  "gt_le_p",
131
138
  "gt_lt_p",
132
139
  "gt_p",
140
+ "has_length_p",
133
141
  "in_p",
142
+ "is_alnum_p",
143
+ "is_alpha_p",
144
+ "is_ascii_p",
134
145
  "is_bool_p",
135
146
  "is_callable_p",
136
147
  "is_complex_p",
137
148
  "is_container_p",
138
149
  "is_datetime_p",
150
+ "is_decimal_p",
139
151
  "is_dict_p",
140
152
  "is_empty_p",
153
+ "is_falsy_p",
141
154
  "is_finite_p",
142
155
  "is_float_p",
143
156
  "is_hashable_p",
144
- "is_instance_p",
157
+ "is_identifier_p",
145
158
  "is_inf_p",
159
+ "is_instance_p",
146
160
  "is_int_p",
147
- "is_iterable_p",
148
161
  "is_iterable_of_p",
149
- "is_list_p",
162
+ "is_iterable_p",
150
163
  "is_list_of_p",
164
+ "is_list_p",
165
+ "is_lower_p",
151
166
  "is_none_p",
152
167
  "is_not_none_p",
153
168
  "is_predicate_p",
154
- "is_set_p",
169
+ "is_range_p",
155
170
  "is_set_of_p",
171
+ "is_set_p",
156
172
  "is_str_p",
173
+ "is_subset_p",
174
+ "is_superset_p",
175
+ "is_real_subset_p",
176
+ "is_real_superset_p",
177
+ "is_title_p",
157
178
  "is_truthy_p",
158
- "is_tuple_p",
159
179
  "is_tuple_of_p",
180
+ "is_tuple_p",
181
+ "is_upper_p",
160
182
  "is_uuid_p",
161
183
  "lazy_p",
162
184
  "le_p",
@@ -174,3 +196,14 @@ __all__ = [
174
196
  "to_json",
175
197
  "zero_p",
176
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
+ )
@@ -10,11 +10,8 @@ class AllPredicate[T](Predicate[T]):
10
10
 
11
11
  predicate: Predicate[T]
12
12
 
13
- def __call__(self, iter: Iterable[T]) -> bool:
14
- return all(self.predicate(x) for x in iter)
15
-
16
- def __eq__(self, other: object) -> bool:
17
- return isinstance(other, AllPredicate) and self.predicate == other.predicate
13
+ def __call__(self, iterable: Iterable[T]) -> bool:
14
+ return all(self.predicate(x) for x in iterable)
18
15
 
19
16
  def __repr__(self) -> str:
20
17
  return f"all({repr(self.predicate)})"
@@ -10,11 +10,8 @@ class AnyPredicate[T](Predicate[T]):
10
10
 
11
11
  predicate: Predicate[T]
12
12
 
13
- def __call__(self, iter: Iterable[T]) -> bool:
14
- return any(self.predicate(x) for x in iter)
15
-
16
- def __eq__(self, other: object) -> bool:
17
- return isinstance(other, AnyPredicate) and self.predicate == other.predicate
13
+ def __call__(self, iterable: Iterable[T]) -> bool:
14
+ return any(self.predicate(x) for x in iterable)
18
15
 
19
16
  def __repr__(self) -> str:
20
17
  return f"any({repr(self.predicate)})"
@@ -25,17 +25,23 @@ from predicate.predicate import (
25
25
  )
26
26
  from predicate.range_predicate import GeLePredicate, GeLtPredicate, GtLePredicate, GtLtPredicate
27
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
+ )
28
36
  from predicate.standard_predicates import (
29
37
  EqPredicate,
30
38
  FnPredicate,
31
39
  GePredicate,
32
40
  GtPredicate,
33
- InPredicate,
34
41
  IsNonePredicate,
35
42
  LePredicate,
36
43
  LtPredicate,
37
44
  NePredicate,
38
- NotInPredicate,
39
45
  )
40
46
  from predicate.tee_predicate import TeePredicate
41
47
  from predicate.this_predicate import ThisPredicate, find_this_predicate
@@ -61,46 +67,52 @@ def to_dot(predicate: Predicate, predicate_string: str = "", show_optimized: boo
61
67
  return dot
62
68
 
63
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
+
64
76
  def render(dot, predicate: Predicate, node_nr):
65
77
  node_predicate_mapping: dict[str, Predicate] = {}
66
78
 
67
- def _add_node(name: str, *, label: str, predicate: Predicate):
79
+ def _add_node(name: str, *, label: str, predicate: Predicate) -> str:
68
80
  node = next(node_nr)
69
81
  unique_name = f"{name}_{node}"
70
82
  dot.node(unique_name, label=label)
71
83
  node_predicate_mapping[unique_name] = predicate
72
84
  return unique_name
73
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
+
74
98
  def to_value(predicate: Predicate):
75
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)
76
102
 
77
103
  match predicate:
78
104
  case AllPredicate(all_predicate):
79
- node = add_node("all", label="∀")
80
- child = to_value(all_predicate)
81
- dot.edge(node, child)
82
- return node
105
+ return add_node_with_child("all", label="∀", child=all_predicate)
83
106
  case AlwaysFalsePredicate():
84
107
  return add_node("F", label="false")
85
108
  case AlwaysTruePredicate():
86
109
  return add_node("T", label="true")
87
110
  case AndPredicate(left, right):
88
- node = add_node("and", label="∧")
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
111
+ return add_node_left_right("and", label="∧", left=left, right=right)
94
112
  case AnyPredicate(any_predicate):
95
- node = add_node("any", label="∃")
96
- child = to_value(any_predicate)
97
- dot.edge(node, child)
98
- return node
113
+ return add_node_with_child("any", label="∃", child=any_predicate)
99
114
  case CompPredicate(_fn, comp_predicate):
100
- node = add_node("comp", label="f")
101
- child = to_value(comp_predicate)
102
- dot.edge(node, child)
103
- return node
115
+ return add_node_with_child("comp", label="f", child=comp_predicate)
104
116
  case EqPredicate(v):
105
117
  return add_node("eq", label=f"x = {v}")
106
118
  case IsFalsyPredicate():
@@ -119,17 +131,24 @@ def render(dot, predicate: Predicate, node_nr):
119
131
  case GtPredicate(v):
120
132
  return add_node("gt", label=f"x > {v}")
121
133
  case GtLePredicate(upper, lower):
122
- return add_node("gele", label=f"{lower} ≤ x ≤ {upper}")
134
+ return add_node("gtle", label=f"{lower} ≤ x ≤ {upper}")
123
135
  case GtLtPredicate(upper, lower):
124
- return add_node("gelt", label=f"{lower} ≤ x < {upper}")
136
+ return add_node("gtlt", label=f"{lower} ≤ x < {upper}")
125
137
  case InPredicate(v):
126
- items = ", ".join(str(item) for item in v)
127
- return add_node("in", label=f"x ∈ {{{items}}}")
138
+ return add_node("in", label=f"x {set_to_str(v)}")
128
139
  case IsInstancePredicate(klass):
129
140
  name = klass[0].__name__ # type: ignore
130
141
  return add_node("instance", label=f"is_{name}_p")
131
142
  case IsNonePredicate():
132
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)}")
133
152
  case LazyPredicate(ref):
134
153
  return add_node("lazy", label=ref)
135
154
  case LePredicate(v):
@@ -139,22 +158,13 @@ def render(dot, predicate: Predicate, node_nr):
139
158
  case NamedPredicate(name):
140
159
  return add_node("named", label=name)
141
160
  case NotInPredicate(v):
142
- items = ", ".join(str(item) for item in v)
143
- return add_node("in", label=f"x ∉ {{{items}}}")
161
+ return add_node("in", label=f"x {set_to_str(v)}")
144
162
  case NePredicate(v):
145
163
  return add_node("ne", label=f"x ≠ {v}")
146
164
  case NotPredicate(not_predicate):
147
- child = to_value(not_predicate)
148
- node = add_node("not", label="¬")
149
- dot.edge(node, child)
150
- return node
165
+ return add_node_with_child("not", label="¬", child=not_predicate)
151
166
  case OrPredicate(left, right):
152
- node = add_node("or", label="∨")
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
167
+ return add_node_left_right("or", label="∨", left=left, right=right)
158
168
  case RootPredicate():
159
169
  return add_node("root", label="root")
160
170
  case TeePredicate():
@@ -162,12 +172,7 @@ def render(dot, predicate: Predicate, node_nr):
162
172
  case ThisPredicate():
163
173
  return add_node("this", label="this")
164
174
  case XorPredicate(left, right):
165
- node = add_node("xor", label="⊻")
166
- left_node = to_value(left)
167
- right_node = to_value(right)
168
- dot.edge(node, left_node)
169
- dot.edge(node, right_node)
170
- return node
175
+ return add_node_left_right("xor", label="⊻", left=left, right=right)
171
176
  case _:
172
177
  raise ValueError(f"Unknown predicate type {predicate}")
173
178
 
@@ -176,7 +181,7 @@ def render(dot, predicate: Predicate, node_nr):
176
181
  render_lazy_references(dot, node_predicate_mapping)
177
182
 
178
183
 
179
- def render_lazy_references(dot, node_predicate_mapping):
184
+ def render_lazy_references(dot, node_predicate_mapping) -> None:
180
185
  def find_in_mapping(lookup: Predicate) -> str:
181
186
  return first(node for node, predicate in node_predicate_mapping.items() if predicate == lookup)
182
187
 
@@ -199,14 +204,14 @@ def render_lazy_references(dot, node_predicate_mapping):
199
204
  add_dashed_line(node, this)
200
205
 
201
206
 
202
- def render_original(dot, predicate: Predicate, node_nr):
207
+ def render_original(dot, predicate: Predicate, node_nr) -> None:
203
208
  with dot.subgraph(name="cluster_original") as original:
204
209
  original.attr(style="filled", color="lightgrey")
205
210
  original.attr(label="Original predicate")
206
211
  render(original, predicate, node_nr)
207
212
 
208
213
 
209
- def render_optimized(dot, predicate: Predicate, node_nr):
214
+ def render_optimized(dot, predicate: Predicate, node_nr) -> None:
210
215
  optimized_predicate = optimize(predicate)
211
216
 
212
217
  with dot.subgraph(name="cluster_optimized") as optimized:
@@ -10,6 +10,7 @@ from more_itertools import random_combination_with_replacement, take
10
10
  from predicate.all_predicate import AllPredicate
11
11
  from predicate.generator.helpers import (
12
12
  generate_anys,
13
+ generate_ints,
13
14
  generate_strings,
14
15
  generate_uuids,
15
16
  random_anys,
@@ -24,15 +25,20 @@ from predicate.predicate import (
24
25
  AndPredicate,
25
26
  EqPredicate,
26
27
  GePredicate,
28
+ GtPredicate,
29
+ IsEmptyPredicate,
27
30
  IsFalsyPredicate,
28
31
  IsNonePredicate,
29
32
  IsNotNonePredicate,
30
33
  IsTruthyPredicate,
34
+ NePredicate,
31
35
  NotPredicate,
32
36
  OrPredicate,
33
37
  Predicate,
34
38
  always_true_p,
35
39
  )
40
+ from predicate.set_of_predicate import SetOfPredicate
41
+ from predicate.set_predicates import InPredicate
36
42
 
37
43
 
38
44
  @singledispatch
@@ -58,8 +64,8 @@ def generate_and(predicate: AndPredicate) -> Iterator:
58
64
  if optimize(predicate) == always_true_p:
59
65
  yield from []
60
66
  else:
61
- yield from (item for item in generate_false(predicate.left) if not predicate.right(item))
62
- yield from (item for item in generate_false(predicate.right) if not predicate.left(item))
67
+ yield from (item for item in generate_false(predicate.left))
68
+ yield from (item for item in generate_false(predicate.right))
63
69
 
64
70
 
65
71
  @generate_false.register
@@ -92,14 +98,51 @@ def generate_ge(predicate: GePredicate) -> Iterator:
92
98
  yield from generate_uuids(NotPredicate(predicate=predicate))
93
99
 
94
100
 
101
+ @generate_false.register
102
+ def generate_gt(predicate: GtPredicate) -> Iterator:
103
+ match predicate.v:
104
+ case datetime() as dt:
105
+ yield from (dt - timedelta(days=days) for days in range(0, 5))
106
+ case float():
107
+ yield from random_floats(upper=predicate.v)
108
+ case int():
109
+ yield from random_ints(upper=predicate.v)
110
+ case str():
111
+ yield from generate_strings(NotPredicate(predicate=predicate))
112
+ case uuid.UUID():
113
+ yield from generate_uuids(NotPredicate(predicate=predicate))
114
+
115
+
95
116
  @generate_false.register
96
117
  def generate_falsy(_predicate: IsFalsyPredicate) -> Iterator:
97
118
  yield from generate_anys(IsTruthyPredicate())
98
119
 
99
120
 
121
+ @generate_false.register
122
+ def generate_in(predicate: InPredicate) -> Iterator:
123
+ # TODO: combine with generate_not_in true
124
+ for item in predicate.v:
125
+ match item:
126
+ case int():
127
+ yield from generate_ints(NotPredicate(predicate=predicate))
128
+ case str():
129
+ yield from generate_strings(NotPredicate(predicate=predicate))
130
+
131
+
132
+ @generate_false.register
133
+ def generate_is_empty(_predicate: IsEmptyPredicate) -> Iterator:
134
+ # TODO: add generic helper function
135
+ yield from ([1], {1, 2, 3}, (1,), "aap")
136
+
137
+
138
+ @generate_false.register
139
+ def generate_ne(predicate: NePredicate) -> Iterator:
140
+ yield from predicate.v
141
+
142
+
100
143
  @generate_false.register
101
144
  def generate_none(_predicate: IsNonePredicate) -> Iterator:
102
- yield generate_anys(IsNotNonePredicate())
145
+ yield from generate_anys(IsNotNonePredicate())
103
146
 
104
147
 
105
148
  @generate_false.register
@@ -122,3 +165,12 @@ def generate_is_instance_p(predicate: IsInstancePredicate) -> Iterator:
122
165
  def generate_or(predicate: OrPredicate) -> Iterator:
123
166
  yield from (item for item in generate_false(predicate.left) if not predicate.right(item))
124
167
  yield from (item for item in generate_false(predicate.right) if not predicate.left(item))
168
+
169
+
170
+ @generate_false.register
171
+ def generate_set_of_p(set_of_predicate: SetOfPredicate) -> Iterator:
172
+ predicate = set_of_predicate.predicate
173
+
174
+ values = take(10, generate_false(predicate))
175
+
176
+ yield set(random_combination_with_replacement(values, 5))
@@ -4,15 +4,18 @@ import uuid
4
4
  from collections.abc import Iterator
5
5
  from datetime import datetime, timedelta
6
6
  from functools import singledispatch
7
+ from itertools import cycle
7
8
 
8
9
  import exrex # type: ignore
9
- from more_itertools import interleave, random_combination_with_replacement, take
10
+ from more_itertools import interleave, powerset_of_sets, random_combination_with_replacement, take
10
11
 
11
12
  from predicate.any_predicate import AnyPredicate
12
13
  from predicate.generator.helpers import (
14
+ generate_anys,
13
15
  generate_ints,
14
16
  generate_strings,
15
17
  generate_uuids,
18
+ random_anys,
16
19
  random_complex_numbers,
17
20
  random_datetimes,
18
21
  random_dicts,
@@ -21,6 +24,7 @@ from predicate.generator.helpers import (
21
24
  random_strings,
22
25
  random_uuids,
23
26
  )
27
+ from predicate.has_key_predicate import HasKeyPredicate
24
28
  from predicate.is_instance_predicate import IsInstancePredicate
25
29
  from predicate.optimizer.predicate_optimizer import optimize
26
30
  from predicate.predicate import (
@@ -30,7 +34,7 @@ from predicate.predicate import (
30
34
  EqPredicate,
31
35
  GePredicate,
32
36
  GtPredicate,
33
- InPredicate,
37
+ IsEmptyPredicate,
34
38
  IsFalsyPredicate,
35
39
  IsNonePredicate,
36
40
  IsNotNonePredicate,
@@ -38,13 +42,15 @@ from predicate.predicate import (
38
42
  LePredicate,
39
43
  LtPredicate,
40
44
  NePredicate,
41
- NotInPredicate,
42
45
  OrPredicate,
43
46
  Predicate,
44
47
  always_false_p,
45
48
  )
46
49
  from predicate.regex_predicate import RegexPredicate
50
+ from predicate.set_of_predicate import SetOfPredicate
51
+ from predicate.set_predicates import InPredicate, IsRealSubsetPredicate, IsSubsetPredicate, NotInPredicate
47
52
  from predicate.standard_predicates import AllPredicate
53
+ from predicate.tuple_of_predicate import TupleOfPredicate
48
54
 
49
55
 
50
56
  @singledispatch
@@ -126,6 +132,13 @@ def generate_gt(predicate: GtPredicate) -> Iterator:
126
132
  yield from generate_uuids(predicate)
127
133
 
128
134
 
135
+ @generate_true.register
136
+ def generate_has_key(predicate: HasKeyPredicate) -> Iterator:
137
+ key = predicate.key
138
+ for random_dict, value in zip(random_dicts(), random_anys(), strict=False):
139
+ yield random_dict | {key: value}
140
+
141
+
129
142
  @generate_true.register
130
143
  def generate_le(predicate: LePredicate) -> Iterator:
131
144
  match predicate.v:
@@ -141,11 +154,26 @@ def generate_le(predicate: LePredicate) -> Iterator:
141
154
  yield from generate_uuids(predicate)
142
155
 
143
156
 
157
+ @generate_true.register
158
+ def generate_subset(predicate: IsSubsetPredicate) -> Iterator:
159
+ yield from powerset_of_sets(predicate.v)
160
+
161
+
162
+ @generate_true.register
163
+ def generate_real_subset(predicate: IsRealSubsetPredicate) -> Iterator:
164
+ yield from (v for v in powerset_of_sets(predicate.v) if v != predicate.v)
165
+
166
+
144
167
  @generate_true.register
145
168
  def generate_in(predicate: InPredicate) -> Iterator:
146
169
  yield from predicate.v
147
170
 
148
171
 
172
+ @generate_true.register
173
+ def generate_is_empty(_predicate: IsEmptyPredicate) -> Iterator:
174
+ yield from ([], {}, (), "", set())
175
+
176
+
149
177
  @generate_true.register
150
178
  def generate_lt(predicate: LtPredicate) -> Iterator:
151
179
  match predicate.v:
@@ -182,8 +210,8 @@ def generate_not_in(predicate: NotInPredicate) -> Iterator:
182
210
 
183
211
 
184
212
  @generate_true.register
185
- def generate_not_none(_predicate: IsNotNonePredicate) -> Iterator:
186
- yield from ("foo", 3.14, 42)
213
+ def generate_not_none(predicate: IsNotNonePredicate) -> Iterator:
214
+ yield from generate_anys(predicate)
187
215
 
188
216
 
189
217
  @generate_true.register
@@ -212,7 +240,7 @@ def generate_is_instance_p(predicate: IsInstancePredicate) -> Iterator:
212
240
  if klass is str:
213
241
  yield from random_strings()
214
242
  elif klass is bool:
215
- yield from (False, True)
243
+ yield from cycle((False, True))
216
244
  elif klass is complex:
217
245
  yield from random_complex_numbers()
218
246
  elif klass == datetime:
@@ -239,3 +267,19 @@ def generate_any_p(any_predicate: AnyPredicate) -> Iterator:
239
267
  yield random_combination_with_replacement(values, 5)
240
268
 
241
269
  yield set(random_combination_with_replacement(values, 5))
270
+
271
+
272
+ @generate_true.register
273
+ def generate_tuple_of_p(tuple_of_predicate: TupleOfPredicate) -> Iterator:
274
+ predicates = tuple_of_predicate.predicates
275
+
276
+ yield from zip(*(generate_true(predicate) for predicate in predicates), strict=False)
277
+
278
+
279
+ @generate_true.register
280
+ def generate_set_of_p(set_of_predicate: SetOfPredicate) -> Iterator:
281
+ predicate = set_of_predicate.predicate
282
+
283
+ values = take(10, generate_true(predicate))
284
+
285
+ yield set(random_combination_with_replacement(values, 5))
@@ -47,18 +47,17 @@ def random_ints(lower: int = -sys.maxsize, upper: int = sys.maxsize) -> Iterator
47
47
  # yield lower
48
48
  # yield upper
49
49
  # TODO: maybe first generate_true some smaller ints
50
- while True:
51
- low = max(-1, lower)
52
- high = min(1, upper)
53
- if high >= low:
54
- yield from (random.randint(low, high) for _ in range(0, 1))
55
50
 
56
- low = max(-10, lower)
57
- high = min(10, upper)
51
+ def between(limit: int) -> Iterator[int]:
52
+ low = max(-limit, lower)
53
+ high = min(limit, upper)
58
54
  if high >= low:
59
- yield from (random.randint(low, high) for _ in range(0, 10))
55
+ yield from (random.randint(low, high) for _ in range(0, limit))
60
56
 
61
- # yield random.randint(lower, upper)
57
+ while True:
58
+ yield from between(1)
59
+ yield from between(10)
60
+ yield from between(100)
62
61
 
63
62
 
64
63
  def random_uuids() -> Iterator[UUID]:
@@ -1,11 +1,10 @@
1
1
  from dataclasses import dataclass
2
- from typing import Any
3
2
 
4
3
  from predicate.predicate import Predicate
5
4
 
6
5
 
7
6
  @dataclass
8
- class HasKeyPredicate[T](Predicate[dict[T, Any]]):
7
+ class HasKeyPredicate[T](Predicate[T]):
9
8
  """A predicate class that models the has key."""
10
9
 
11
10
  key: T
@@ -13,8 +12,5 @@ class HasKeyPredicate[T](Predicate[dict[T, Any]]):
13
12
  def __call__(self, v: dict) -> bool:
14
13
  return self.key in v.keys()
15
14
 
16
- def __eq__(self, other: object) -> bool:
17
- return isinstance(other, HasKeyPredicate) and self.key == other.key
18
-
19
15
  def __repr__(self) -> str:
20
16
  return f'has_key_p("{self.key}")'