py-predicate 0.7__tar.gz → 0.8__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 (77) hide show
  1. {py_predicate-0.7 → py_predicate-0.8}/PKG-INFO +1 -1
  2. {py_predicate-0.7 → py_predicate-0.8}/predicate/__init__.py +2 -0
  3. {py_predicate-0.7 → py_predicate-0.8}/predicate/all_predicate.py +6 -2
  4. {py_predicate-0.7 → py_predicate-0.8}/predicate/always_false_predicate.py +5 -1
  5. {py_predicate-0.7 → py_predicate-0.8}/predicate/always_true_predicate.py +5 -1
  6. {py_predicate-0.7 → py_predicate-0.8}/predicate/any_predicate.py +6 -2
  7. py_predicate-0.8/predicate/constructor/construct.py +84 -0
  8. py_predicate-0.8/predicate/constructor/helpers.py +48 -0
  9. py_predicate-0.8/predicate/constructor/mutate.py +78 -0
  10. {py_predicate-0.7 → py_predicate-0.8}/predicate/eq_predicate.py +6 -2
  11. {py_predicate-0.7 → py_predicate-0.8}/predicate/ge_predicate.py +6 -2
  12. {py_predicate-0.7 → py_predicate-0.8}/predicate/generator/generate_false.py +12 -3
  13. py_predicate-0.8/predicate/generator/generate_predicate.py +164 -0
  14. {py_predicate-0.7 → py_predicate-0.8}/predicate/generator/generate_true.py +16 -9
  15. {py_predicate-0.7 → py_predicate-0.8}/predicate/generator/helpers.py +72 -25
  16. {py_predicate-0.7 → py_predicate-0.8}/predicate/gt_predicate.py +6 -2
  17. {py_predicate-0.7 → py_predicate-0.8}/predicate/is_instance_predicate.py +8 -4
  18. py_predicate-0.8/predicate/is_predicate_of_p.py +30 -0
  19. {py_predicate-0.7 → py_predicate-0.8}/predicate/le_predicate.py +6 -2
  20. {py_predicate-0.7 → py_predicate-0.8}/predicate/list_of_predicate.py +4 -0
  21. {py_predicate-0.7 → py_predicate-0.8}/predicate/lt_predicate.py +6 -2
  22. {py_predicate-0.7 → py_predicate-0.8}/predicate/ne_predicate.py +6 -2
  23. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/or_optimizer.py +8 -0
  24. {py_predicate-0.7 → py_predicate-0.8}/predicate/parser.py +18 -3
  25. {py_predicate-0.7 → py_predicate-0.8}/predicate/predicate.py +26 -2
  26. {py_predicate-0.7 → py_predicate-0.8}/predicate/range_predicate.py +24 -8
  27. {py_predicate-0.7 → py_predicate-0.8}/predicate/set_predicates.py +17 -1
  28. {py_predicate-0.7 → py_predicate-0.8}/predicate/standard_predicates.py +7 -6
  29. {py_predicate-0.7 → py_predicate-0.8}/pyproject.toml +1 -1
  30. py_predicate-0.7/predicate/constructor/construct.py +0 -60
  31. {py_predicate-0.7 → py_predicate-0.8}/README.md +0 -0
  32. {py_predicate-0.7 → py_predicate-0.8}/predicate/comp_predicate.py +0 -0
  33. {py_predicate-0.7 → py_predicate-0.8}/predicate/constructor/__init__.py +0 -0
  34. {py_predicate-0.7 → py_predicate-0.8}/predicate/dict_of_predicate.py +0 -0
  35. {py_predicate-0.7 → py_predicate-0.8}/predicate/explain.py +0 -0
  36. {py_predicate-0.7 → py_predicate-0.8}/predicate/fn_predicate.py +0 -0
  37. {py_predicate-0.7 → py_predicate-0.8}/predicate/formatter/__init__.py +0 -0
  38. {py_predicate-0.7 → py_predicate-0.8}/predicate/formatter/format_dot.py +0 -0
  39. {py_predicate-0.7 → py_predicate-0.8}/predicate/formatter/format_json.py +0 -0
  40. {py_predicate-0.7 → py_predicate-0.8}/predicate/formatter/format_latex.py +0 -0
  41. {py_predicate-0.7 → py_predicate-0.8}/predicate/formatter/helpers.py +0 -0
  42. {py_predicate-0.7 → py_predicate-0.8}/predicate/generator/__init__.py +0 -0
  43. {py_predicate-0.7 → py_predicate-0.8}/predicate/has_key_predicate.py +0 -0
  44. {py_predicate-0.7 → py_predicate-0.8}/predicate/has_length_predicate.py +0 -0
  45. {py_predicate-0.7 → py_predicate-0.8}/predicate/has_path_predicate.py +0 -0
  46. {py_predicate-0.7 → py_predicate-0.8}/predicate/helpers.py +0 -0
  47. {py_predicate-0.7 → py_predicate-0.8}/predicate/implies.py +0 -0
  48. {py_predicate-0.7 → py_predicate-0.8}/predicate/implies_predicate.py +0 -0
  49. {py_predicate-0.7 → py_predicate-0.8}/predicate/in_predicate_predicate.py +0 -0
  50. {py_predicate-0.7 → py_predicate-0.8}/predicate/ip_address_predicates.py +0 -0
  51. {py_predicate-0.7 → py_predicate-0.8}/predicate/is_callable_predicate.py +0 -0
  52. {py_predicate-0.7 → py_predicate-0.8}/predicate/is_empty_predicate.py +0 -0
  53. {py_predicate-0.7 → py_predicate-0.8}/predicate/is_falsy_predicate.py +0 -0
  54. {py_predicate-0.7 → py_predicate-0.8}/predicate/is_lambda_predicate.py +0 -0
  55. {py_predicate-0.7 → py_predicate-0.8}/predicate/is_none_predicate.py +0 -0
  56. {py_predicate-0.7 → py_predicate-0.8}/predicate/is_not_none_predicate.py +0 -0
  57. {py_predicate-0.7 → py_predicate-0.8}/predicate/is_truthy_predicate.py +0 -0
  58. {py_predicate-0.7 → py_predicate-0.8}/predicate/lazy_predicate.py +0 -0
  59. {py_predicate-0.7 → py_predicate-0.8}/predicate/named_predicate.py +0 -0
  60. {py_predicate-0.7 → py_predicate-0.8}/predicate/negate.py +0 -0
  61. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/__init__.py +0 -0
  62. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/all_optimizer.py +0 -0
  63. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/and_optimizer.py +0 -0
  64. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/any_optimizer.py +0 -0
  65. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/in_optimizer.py +0 -0
  66. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/not_optimizer.py +0 -0
  67. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/predicate_optimizer.py +0 -0
  68. {py_predicate-0.7 → py_predicate-0.8}/predicate/optimizer/xor_optimizer.py +0 -0
  69. {py_predicate-0.7 → py_predicate-0.8}/predicate/property_predicate.py +0 -0
  70. {py_predicate-0.7 → py_predicate-0.8}/predicate/regex_predicate.py +0 -0
  71. {py_predicate-0.7 → py_predicate-0.8}/predicate/root_predicate.py +0 -0
  72. {py_predicate-0.7 → py_predicate-0.8}/predicate/set_of_predicate.py +0 -0
  73. {py_predicate-0.7 → py_predicate-0.8}/predicate/str_predicates.py +0 -0
  74. {py_predicate-0.7 → py_predicate-0.8}/predicate/tee_predicate.py +0 -0
  75. {py_predicate-0.7 → py_predicate-0.8}/predicate/this_predicate.py +0 -0
  76. {py_predicate-0.7 → py_predicate-0.8}/predicate/truth_table.py +0 -0
  77. {py_predicate-0.7 → py_predicate-0.8}/predicate/tuple_of_predicate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: py_predicate
3
- Version: 0.7
3
+ Version: 0.8
4
4
  Summary: Module to create composable predicates
5
5
  Author-email: Maurits Rijk <maurits.rijk@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -56,6 +56,7 @@ from predicate.standard_predicates import (
56
56
  is_nan_p,
57
57
  is_none_p,
58
58
  is_not_none_p,
59
+ is_predicate_of_p,
59
60
  is_predicate_p,
60
61
  is_range_p,
61
62
  is_set_of_p,
@@ -133,6 +134,7 @@ __all__ = [
133
134
  "is_none_p",
134
135
  "is_not_empty_p",
135
136
  "is_not_none_p",
137
+ "is_predicate_of_p",
136
138
  "is_predicate_p",
137
139
  "is_range_p",
138
140
  "is_set_of_p",
@@ -18,10 +18,14 @@ class AllPredicate[T](Predicate[T]):
18
18
  return predicate in self.predicate
19
19
 
20
20
  def __repr__(self) -> str:
21
- return f"all({repr(self.predicate)})"
21
+ return f"all_p({self.predicate!r})"
22
+
23
+ @override
24
+ def get_klass(self) -> type:
25
+ return Predicate[self.predicate.klass] # type: ignore[name-defined]
22
26
 
23
27
  @override
24
28
  def explain_failure(self, iterable: Iterable[T]) -> dict:
25
29
  fail = first_false(iterable, self.predicate)
26
30
 
27
- return {"reason": f"Item '{fail}' didn't match predicate {repr(self.predicate)}"}
31
+ return {"reason": f"Item '{fail}' didn't match predicate {self.predicate!r}"}
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import Final, override
2
+ from typing import Any, Final, 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 get_klass(self) -> type:
23
+ return type(Any)
24
+
21
25
 
22
26
  always_false_p: Final[AlwaysFalsePredicate] = AlwaysFalsePredicate()
23
27
  """Predicate that always evaluates to False."""
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import Final
2
+ from typing import Any, Final, 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 get_klass(self) -> type:
19
+ return type(Any)
20
+
17
21
 
18
22
  always_true_p: Final[AlwaysTruePredicate] = AlwaysTruePredicate()
19
23
  """Predicate that always evaluates to True."""
@@ -17,8 +17,12 @@ class AnyPredicate[T](Predicate[T]):
17
17
  return predicate in self.predicate
18
18
 
19
19
  def __repr__(self) -> str:
20
- return f"any({repr(self.predicate)})"
20
+ return f"any_p({self.predicate!r})"
21
+
22
+ @override
23
+ def get_klass(self) -> type:
24
+ return Predicate[self.predicate.klass] # type: ignore[name-defined]
21
25
 
22
26
  @override
23
27
  def explain_failure(self, iterable: Iterable[T]) -> dict:
24
- return {"reason": f"No item matches predicate {repr(self.predicate)}"}
28
+ return {"reason": f"No item matches predicate {self.predicate!r}"}
@@ -0,0 +1,84 @@
1
+ from typing import Iterator
2
+
3
+ from more_itertools import first, gray_product, take
4
+
5
+ from predicate import (
6
+ always_false_p,
7
+ always_true_p,
8
+ is_datetime_p,
9
+ is_falsy_p,
10
+ is_float_p,
11
+ is_int_p,
12
+ is_not_none_p,
13
+ is_set_p,
14
+ is_str_p,
15
+ is_truthy_p,
16
+ )
17
+ from predicate.constructor.helpers import perfect_match, sort_by_match
18
+ from predicate.constructor.mutate import mutations
19
+ from predicate.predicate import Predicate
20
+ from predicate.standard_predicates import (
21
+ ge_p,
22
+ gt_p,
23
+ is_bool_p,
24
+ is_dict_p,
25
+ is_list_p,
26
+ is_none_p,
27
+ le_p,
28
+ lt_p,
29
+ ne_p,
30
+ zero_p,
31
+ )
32
+
33
+
34
+ def construct(false_set: list, true_set: list, attempts: int = 30) -> Predicate | None:
35
+ predicates = initial_predicates()
36
+
37
+ while attempts:
38
+ sorted_by_match = sort_by_match(list(predicates), false_set=false_set, true_set=true_set)
39
+
40
+ if perfect_match(matched := first(sorted_by_match), false_set=false_set, true_set=true_set):
41
+ return matched
42
+
43
+ predicates = create_mutations(take(20, sorted_by_match), false_set=false_set, true_set=true_set)
44
+
45
+ attempts -= 1
46
+
47
+ return None
48
+
49
+
50
+ def create_mutations(candidates: list[Predicate], false_set: list, true_set: list) -> Iterator[Predicate]:
51
+ for candidate in candidates:
52
+ yield from mutations(candidate, false_set=false_set, true_set=true_set)
53
+
54
+ pairs = gray_product(candidates, candidates)
55
+ for pair in pairs:
56
+ left, right = pair
57
+ if left != right:
58
+ yield left | right
59
+ yield left & right
60
+ yield left ^ right
61
+
62
+
63
+ def initial_predicates() -> Iterator[Predicate]:
64
+ # TODO: probably import from __init__
65
+ yield always_false_p
66
+ yield always_true_p
67
+ yield ge_p(0)
68
+ yield gt_p(0)
69
+ yield is_bool_p
70
+ yield is_datetime_p
71
+ yield is_dict_p
72
+ yield is_falsy_p
73
+ yield is_float_p
74
+ yield is_int_p
75
+ yield is_list_p
76
+ yield is_none_p
77
+ yield is_not_none_p
78
+ yield is_set_p
79
+ yield is_str_p
80
+ yield is_truthy_p
81
+ yield le_p(0)
82
+ yield lt_p(0)
83
+ yield ne_p(0)
84
+ yield zero_p
@@ -0,0 +1,48 @@
1
+ from typing import Any
2
+
3
+ from predicate.predicate import Predicate
4
+
5
+
6
+ def safe_call_false(predicate: Predicate, value: Any) -> bool:
7
+ # For performance reasons type checking is not part of the predicate itself
8
+ try:
9
+ return predicate(value)
10
+ except TypeError:
11
+ return True
12
+
13
+
14
+ def safe_call_true(predicate: Predicate, value: Any) -> bool:
15
+ # For performance reasons type checking is not part of the predicate itself
16
+ try:
17
+ return predicate(value)
18
+ except TypeError:
19
+ return False
20
+
21
+
22
+ def predicate_match(predicate: Predicate, false_set: list, true_set: list) -> dict[str, int]:
23
+ false_misses = sum(safe_call_false(predicate, value) for value in false_set)
24
+ false_matches = len(false_set) - false_misses
25
+
26
+ true_matches = sum(safe_call_true(predicate, value) for value in true_set)
27
+ true_misses = len(true_set) - true_matches
28
+
29
+ return {
30
+ "false_matches": false_matches,
31
+ "false_misses": false_misses,
32
+ "true_matches": true_matches,
33
+ "true_misses": true_misses,
34
+ }
35
+
36
+
37
+ def sort_by_match(predicates: list[Predicate], false_set: list, true_set: list) -> list[Predicate]:
38
+ def best_match(predicate) -> int:
39
+ match = predicate_match(predicate, false_set=false_set, true_set=true_set)
40
+
41
+ return match["false_matches"] + match["true_matches"]
42
+
43
+ return sorted(predicates, key=best_match, reverse=True)
44
+
45
+
46
+ def perfect_match(predicate: Predicate, false_set: list, true_set: list) -> bool:
47
+ match = predicate_match(predicate, false_set=false_set, true_set=true_set)
48
+ return match["false_misses"] + match["true_misses"] == 0
@@ -0,0 +1,78 @@
1
+ from collections.abc import Iterator
2
+ from functools import singledispatch
3
+ from random import choice, randint
4
+
5
+ from predicate import eq_p, ge_p, gt_p, le_p, lt_p, ne_p
6
+ from predicate.eq_predicate import EqPredicate
7
+ from predicate.ge_predicate import GePredicate
8
+ from predicate.gt_predicate import GtPredicate
9
+ from predicate.le_predicate import LePredicate
10
+ from predicate.lt_predicate import LtPredicate
11
+ from predicate.ne_predicate import NePredicate
12
+ from predicate.predicate import Predicate
13
+
14
+
15
+ @singledispatch
16
+ def mutations(predicate: Predicate, false_set: list, true_set: list, nr: int = 3) -> Iterator[Predicate]:
17
+ """Return nr of mutations."""
18
+ yield predicate
19
+
20
+
21
+ def int_value_mutations(n: int, values: list, nr: int) -> Iterator[int]:
22
+ yield choice(values)
23
+ yield n - randint(0, 10)
24
+ yield n + randint(0, 10)
25
+
26
+
27
+ @mutations.register
28
+ def _(predicate: EqPredicate, false_set: list, true_set: list, nr: int = 3) -> Iterator[Predicate]:
29
+ match predicate.v:
30
+ case int(n):
31
+ yield from (eq_p(v) for v in int_value_mutations(n, true_set, nr))
32
+ case _:
33
+ pass
34
+
35
+
36
+ @mutations.register
37
+ def _(predicate: NePredicate, false_set, true_set: list, nr: int = 3) -> Iterator[Predicate]:
38
+ match predicate.v:
39
+ case int(n):
40
+ yield from (ne_p(v) for v in int_value_mutations(n, false_set, nr))
41
+ case _:
42
+ pass
43
+
44
+
45
+ @mutations.register
46
+ def _(predicate: GePredicate, false_set, true_set: list, nr: int = 3) -> Iterator[Predicate]:
47
+ match predicate.v:
48
+ case int(n):
49
+ yield from (ge_p(v) for v in int_value_mutations(n, true_set, nr))
50
+ case _:
51
+ pass
52
+
53
+
54
+ @mutations.register
55
+ def _(predicate: GtPredicate, false_set, true_set: list, nr: int = 3) -> Iterator[Predicate]:
56
+ match predicate.v:
57
+ case int(n):
58
+ yield from (gt_p(v) for v in int_value_mutations(n, true_set, nr))
59
+ case _:
60
+ pass
61
+
62
+
63
+ @mutations.register
64
+ def _(predicate: LePredicate, false_set, true_set: list, nr: int = 3) -> Iterator[Predicate]:
65
+ match predicate.v:
66
+ case int(n):
67
+ yield from (le_p(v) for v in int_value_mutations(n, true_set, nr))
68
+ case _:
69
+ pass
70
+
71
+
72
+ @mutations.register
73
+ def _(predicate: LtPredicate, false_set, true_set: list, nr: int = 3) -> Iterator[Predicate]:
74
+ match predicate.v:
75
+ case int(n):
76
+ yield from (lt_p(v) for v in int_value_mutations(n, true_set, nr))
77
+ case _:
78
+ pass
@@ -14,8 +14,12 @@ class EqPredicate[T](Predicate[T]):
14
14
  return x == self.v
15
15
 
16
16
  def __repr__(self) -> str:
17
- return f"eq_p({self.v})"
17
+ return f"eq_p({self.v!r})"
18
+
19
+ @override
20
+ def get_klass(self) -> type:
21
+ return type(self.v)
18
22
 
19
23
  @override
20
24
  def explain_failure(self, x: T) -> dict:
21
- return {"reason": f"{x} is not equal to {self.v}"}
25
+ return {"reason": f"{x} is not equal to {self.v!r}"}
@@ -14,8 +14,12 @@ class GePredicate[T](Predicate[T]):
14
14
  return x >= self.v
15
15
 
16
16
  def __repr__(self) -> str:
17
- return f"ge_p({self.v})"
17
+ return f"ge_p({self.v!r})"
18
+
19
+ @override
20
+ def get_klass(self) -> type:
21
+ return type(self.v)
18
22
 
19
23
  @override
20
24
  def explain_failure(self, x: T) -> dict:
21
- return {"reason": f"{x} is not greater or equal to {self.v}"}
25
+ return {"reason": f"{x} is not greater or equal to {self.v!r}"}
@@ -331,9 +331,18 @@ def generate_is_instance_p(predicate: IsInstancePredicate) -> Iterator:
331
331
 
332
332
  @generate_false.register
333
333
  def generate_or(predicate: OrPredicate) -> Iterator:
334
- iterable_1 = (item for item in generate_false(predicate.left) if not predicate.right(item))
335
- iterable_2 = (item for item in generate_false(predicate.right) if not predicate.left(item))
336
- yield from random_first_from_iterables(iterable_1, iterable_2)
334
+ attempts = 100
335
+
336
+ try_left = (item for item in take(attempts, generate_false(predicate.left)) if not predicate.right(item))
337
+ try_right = (item for item in take(attempts, generate_false(predicate.right)) if not predicate.left(item))
338
+
339
+ range_1 = (item for item in generate_false(predicate.left) if not predicate.right(item)) if try_left else ()
340
+ range_2 = (item for item in generate_false(predicate.right) if not predicate.left(item)) if try_right else ()
341
+
342
+ if range_1 or range_2:
343
+ yield from random_first_from_iterables(range_1, range_2)
344
+
345
+ raise ValueError(f"Couldn't generate values that statisfy {predicate}")
337
346
 
338
347
 
339
348
  @generate_false.register
@@ -0,0 +1,164 @@
1
+ from typing import Any, Iterator
2
+
3
+ from predicate.all_predicate import AllPredicate
4
+ from predicate.eq_predicate import EqPredicate
5
+ from predicate.ge_predicate import GePredicate
6
+ from predicate.gt_predicate import GtPredicate
7
+ from predicate.le_predicate import LePredicate
8
+ from predicate.lt_predicate import LtPredicate
9
+ from predicate.ne_predicate import NePredicate
10
+ from predicate.predicate import AndPredicate, NotPredicate, OrPredicate, Predicate, XorPredicate
11
+ from predicate.range_predicate import GeLePredicate, GeLtPredicate, GtLePredicate, GtLtPredicate
12
+ from predicate.set_predicates import InPredicate, NotInPredicate
13
+
14
+
15
+ def generate_predicate(predicate_type: type[Predicate], max_depth: int, klass: type) -> Iterator[Predicate]:
16
+ predicate_type_registry: dict[type, Any] = {
17
+ # TODO: AllPredicate works on iterables
18
+ # AllPredicate: generate_all_predicates,
19
+ AndPredicate: generate_and_predicates,
20
+ EqPredicate: generate_eq_predicates,
21
+ GeLePredicate: generate_ge_le_predicates,
22
+ GeLtPredicate: generate_ge_lt_predicates,
23
+ GePredicate: generate_ge_predicates,
24
+ GtPredicate: generate_gt_predicates,
25
+ GtLePredicate: generate_gt_le_predicates,
26
+ GtLtPredicate: generate_gt_lt_predicates,
27
+ InPredicate: generate_in_predicates,
28
+ LePredicate: generate_le_predicates,
29
+ LtPredicate: generate_lt_predicates,
30
+ NePredicate: generate_ne_predicates,
31
+ NotInPredicate: generate_not_in_predicates,
32
+ NotPredicate: generate_not_predicates,
33
+ OrPredicate: generate_or_predicates,
34
+ XorPredicate: generate_xor_predicates,
35
+ }
36
+
37
+ if generator := predicate_type_registry.get(predicate_type):
38
+ yield from generator(max_depth=max_depth, klass=klass)
39
+ else:
40
+ yield from []
41
+ # raise ValueError(f"No generator defined for predicate type {predicate_type}")
42
+
43
+
44
+ def generate_random_predicate_pairs(max_depth: int, klass: type) -> Iterator:
45
+ from predicate.generator.helpers import random_predicates
46
+
47
+ left_predicates = random_predicates(max_depth=max_depth - 1, klass=klass)
48
+ right_predicates = random_predicates(max_depth=max_depth - 1, klass=klass)
49
+
50
+ return zip(left_predicates, right_predicates, strict=False)
51
+
52
+
53
+ def generate_all_predicates(max_depth: int, klass: type) -> Iterator:
54
+ if not max_depth:
55
+ return
56
+
57
+ from predicate.generator.helpers import random_predicates
58
+
59
+ predicates = random_predicates(max_depth=max_depth - 1, klass=klass)
60
+
61
+ yield from (AllPredicate(predicate) for predicate in predicates)
62
+
63
+
64
+ def generate_and_predicates(max_depth: int, klass: type) -> Iterator:
65
+ if not max_depth:
66
+ return
67
+
68
+ yield from (left & right for left, right in generate_random_predicate_pairs(max_depth=max_depth, klass=klass))
69
+
70
+
71
+ def generate_eq_predicates(max_depth: int, klass: type) -> Iterator:
72
+ from predicate.generator.helpers import random_values_of_type
73
+
74
+ yield from (EqPredicate(value) for value in random_values_of_type(klass))
75
+
76
+
77
+ def generate_ge_le_predicates(max_depth: int, klass: type) -> Iterator:
78
+ from predicate.generator.helpers import random_constrained_pairs_of_type
79
+
80
+ yield from (GeLePredicate(lower=lower, upper=upper) for lower, upper in random_constrained_pairs_of_type(klass))
81
+
82
+
83
+ def generate_ge_lt_predicates(max_depth: int, klass: type) -> Iterator:
84
+ from predicate.generator.helpers import random_constrained_pairs_of_type
85
+
86
+ yield from (GeLtPredicate(lower=lower, upper=upper) for lower, upper in random_constrained_pairs_of_type(klass))
87
+
88
+
89
+ def generate_gt_le_predicates(max_depth: int, klass: type) -> Iterator:
90
+ from predicate.generator.helpers import random_constrained_pairs_of_type
91
+
92
+ yield from (GtLePredicate(lower=lower, upper=upper) for lower, upper in random_constrained_pairs_of_type(klass))
93
+
94
+
95
+ def generate_gt_lt_predicates(max_depth: int, klass: type) -> Iterator:
96
+ from predicate.generator.helpers import random_constrained_pairs_of_type
97
+
98
+ yield from (GtLtPredicate(lower=lower, upper=upper) for lower, upper in random_constrained_pairs_of_type(klass))
99
+
100
+
101
+ def generate_ge_predicates(max_depth: int, klass: type) -> Iterator:
102
+ from predicate.generator.helpers import random_constrained_values_of_type
103
+
104
+ yield from (GePredicate(value) for value in random_constrained_values_of_type(klass))
105
+
106
+
107
+ def generate_gt_predicates(max_depth: int, klass: type) -> Iterator:
108
+ from predicate.generator.helpers import random_constrained_values_of_type
109
+
110
+ yield from (GtPredicate(value) for value in random_constrained_values_of_type(klass))
111
+
112
+
113
+ def generate_in_predicates(max_depth: int, klass: type) -> Iterator:
114
+ from predicate.generator.helpers import random_iterables
115
+
116
+ yield from (InPredicate(iterable) for iterable in random_iterables(klass=klass))
117
+
118
+
119
+ def generate_not_in_predicates(max_depth: int, klass: type) -> Iterator:
120
+ from predicate.generator.helpers import random_iterables
121
+
122
+ yield from (NotInPredicate(iterable) for iterable in random_iterables(klass=klass))
123
+
124
+
125
+ def generate_le_predicates(max_depth: int, klass: type) -> Iterator:
126
+ from predicate.generator.helpers import random_constrained_values_of_type
127
+
128
+ yield from (LePredicate(value) for value in random_constrained_values_of_type(klass))
129
+
130
+
131
+ def generate_lt_predicates(max_depth: int, klass: type) -> Iterator:
132
+ from predicate.generator.helpers import random_constrained_values_of_type
133
+
134
+ yield from (LtPredicate(value) for value in random_constrained_values_of_type(klass))
135
+
136
+
137
+ def generate_ne_predicates(max_depth: int, klass: type) -> Iterator:
138
+ from predicate.generator.helpers import random_values_of_type
139
+
140
+ yield from (NePredicate(value) for value in random_values_of_type(klass))
141
+
142
+
143
+ def generate_not_predicates(max_depth: int, klass: type) -> Iterator:
144
+ if not max_depth:
145
+ return
146
+
147
+ from predicate.generator.helpers import random_predicates
148
+
149
+ predicates = random_predicates(max_depth=max_depth - 1, klass=klass)
150
+ yield from (~predicate for predicate in predicates)
151
+
152
+
153
+ def generate_or_predicates(max_depth: int, klass: type) -> Iterator:
154
+ if not max_depth:
155
+ return
156
+
157
+ yield from (left | right for left, right in generate_random_predicate_pairs(max_depth=max_depth, klass=klass))
158
+
159
+
160
+ def generate_xor_predicates(max_depth: int, klass: type) -> Iterator:
161
+ if not max_depth:
162
+ return
163
+
164
+ yield from (left ^ right for left, right in generate_random_predicate_pairs(max_depth=max_depth, klass=klass))
@@ -56,6 +56,7 @@ from predicate.is_falsy_predicate import IsFalsyPredicate
56
56
  from predicate.is_instance_predicate import IsInstancePredicate
57
57
  from predicate.is_none_predicate import IsNonePredicate
58
58
  from predicate.is_not_none_predicate import IsNotNonePredicate
59
+ from predicate.is_predicate_of_p import IsPredicateOfPredicate
59
60
  from predicate.is_truthy_predicate import IsTruthyPredicate
60
61
  from predicate.le_predicate import LePredicate
61
62
  from predicate.list_of_predicate import ListOfPredicate
@@ -119,9 +120,7 @@ def generate_any_p(any_predicate: AnyPredicate, *, min_size: int = 1, max_size:
119
120
  combined_values = false_values + true_values
120
121
 
121
122
  yield random_permutation(combined_values)
122
- yield from set_from_list(
123
- combined_values,
124
- )
123
+ yield from set_from_list(combined_values)
125
124
 
126
125
 
127
126
  @generate_true.register
@@ -219,6 +218,7 @@ def generate_ge(predicate: GePredicate) -> Iterator:
219
218
  case int():
220
219
  yield from random_ints(lower=predicate.v)
221
220
  case str():
221
+ yield v
222
222
  yield from generate_strings(predicate)
223
223
  case UUID():
224
224
  yield from generate_uuids(predicate)
@@ -318,7 +318,7 @@ def generate_lt(predicate: LtPredicate) -> Iterator:
318
318
 
319
319
  @generate_true.register
320
320
  def generate_ne(predicate: NePredicate) -> Iterator:
321
- yield not predicate.v
321
+ yield from generate_anys(predicate)
322
322
 
323
323
 
324
324
  @generate_true.register
@@ -374,8 +374,13 @@ def generate_truthy(_predicate: IsTruthyPredicate) -> Iterator:
374
374
 
375
375
 
376
376
  @generate_true.register
377
- def generate_is_instance_p(predicate: IsInstancePredicate) -> Iterator:
378
- klass = predicate.klass[0] # type: ignore
377
+ def generate_predicate_of(predicate: IsPredicateOfPredicate, **kwargs) -> Iterator:
378
+ yield from random_predicates(**kwargs, klass=predicate.predicate_klass)
379
+
380
+
381
+ @generate_true.register
382
+ def generate_is_instance_p(predicate: IsInstancePredicate, **kwargs) -> Iterator:
383
+ klass = predicate.instance_klass[0] # type: ignore
379
384
 
380
385
  type_registry: dict[Any, Callable[[], Iterator]] = {
381
386
  Callable: random_callables,
@@ -396,9 +401,11 @@ def generate_is_instance_p(predicate: IsInstancePredicate) -> Iterator:
396
401
  }
397
402
 
398
403
  if generator := type_registry.get(klass):
399
- yield from generator()
400
-
401
- raise ValueError(f"No generator found for {klass}")
404
+ yield from generator(**kwargs)
405
+ elif klass == Predicate: # TODO
406
+ yield from random_predicates(**kwargs)
407
+ else:
408
+ raise ValueError(f"No generator found for {klass}")
402
409
 
403
410
 
404
411
  @generate_true.register