py-predicate 0.6__tar.gz → 0.7__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 (75) hide show
  1. {py_predicate-0.6 → py_predicate-0.7}/PKG-INFO +16 -1
  2. {py_predicate-0.6 → py_predicate-0.7}/README.md +15 -0
  3. {py_predicate-0.6 → py_predicate-0.7}/predicate/__init__.py +10 -47
  4. {py_predicate-0.6 → py_predicate-0.7}/predicate/all_predicate.py +6 -4
  5. py_predicate-0.7/predicate/always_false_predicate.py +26 -0
  6. py_predicate-0.7/predicate/always_true_predicate.py +22 -0
  7. {py_predicate-0.6 → py_predicate-0.7}/predicate/any_predicate.py +4 -1
  8. {py_predicate-0.6 → py_predicate-0.7}/predicate/comp_predicate.py +4 -1
  9. {py_predicate-0.6 → py_predicate-0.7}/predicate/constructor/construct.py +13 -2
  10. {py_predicate-0.6 → py_predicate-0.7}/predicate/dict_of_predicate.py +32 -13
  11. {py_predicate-0.6 → py_predicate-0.7}/predicate/eq_predicate.py +1 -1
  12. {py_predicate-0.6 → py_predicate-0.7}/predicate/fn_predicate.py +2 -2
  13. py_predicate-0.7/predicate/formatter/__init__.py +9 -0
  14. {py_predicate-0.6 → py_predicate-0.7}/predicate/formatter/format_dot.py +14 -19
  15. {py_predicate-0.6 → py_predicate-0.7}/predicate/formatter/format_json.py +4 -4
  16. py_predicate-0.7/predicate/formatter/format_latex.py +86 -0
  17. py_predicate-0.7/predicate/formatter/helpers.py +4 -0
  18. {py_predicate-0.6 → py_predicate-0.7}/predicate/ge_predicate.py +1 -1
  19. {py_predicate-0.6 → py_predicate-0.7}/predicate/generator/generate_false.py +108 -67
  20. {py_predicate-0.6 → py_predicate-0.7}/predicate/generator/generate_true.py +113 -57
  21. {py_predicate-0.6 → py_predicate-0.7}/predicate/generator/helpers.py +56 -8
  22. {py_predicate-0.6 → py_predicate-0.7}/predicate/gt_predicate.py +1 -1
  23. {py_predicate-0.6 → py_predicate-0.7}/predicate/has_key_predicate.py +1 -1
  24. {py_predicate-0.6 → py_predicate-0.7}/predicate/has_length_predicate.py +1 -1
  25. py_predicate-0.7/predicate/has_path_predicate.py +50 -0
  26. py_predicate-0.7/predicate/helpers.py +18 -0
  27. {py_predicate-0.6 → py_predicate-0.7}/predicate/implies.py +26 -2
  28. py_predicate-0.7/predicate/implies_predicate.py +26 -0
  29. py_predicate-0.7/predicate/in_predicate_predicate.py +25 -0
  30. py_predicate-0.7/predicate/is_callable_predicate.py +55 -0
  31. {py_predicate-0.6 → py_predicate-0.7}/predicate/is_empty_predicate.py +2 -2
  32. py_predicate-0.7/predicate/is_falsy_predicate.py +19 -0
  33. {py_predicate-0.6 → py_predicate-0.7}/predicate/is_instance_predicate.py +1 -1
  34. py_predicate-0.7/predicate/is_lambda_predicate.py +55 -0
  35. {py_predicate-0.6 → py_predicate-0.7}/predicate/is_none_predicate.py +1 -1
  36. {py_predicate-0.6 → py_predicate-0.7}/predicate/is_not_none_predicate.py +1 -1
  37. py_predicate-0.7/predicate/is_truthy_predicate.py +19 -0
  38. {py_predicate-0.6 → py_predicate-0.7}/predicate/le_predicate.py +1 -1
  39. {py_predicate-0.6 → py_predicate-0.7}/predicate/list_of_predicate.py +8 -6
  40. {py_predicate-0.6 → py_predicate-0.7}/predicate/lt_predicate.py +1 -1
  41. {py_predicate-0.6 → py_predicate-0.7}/predicate/ne_predicate.py +1 -1
  42. {py_predicate-0.6 → py_predicate-0.7}/predicate/negate.py +18 -17
  43. {py_predicate-0.6 → py_predicate-0.7}/predicate/optimizer/all_optimizer.py +3 -7
  44. {py_predicate-0.6 → py_predicate-0.7}/predicate/optimizer/and_optimizer.py +3 -9
  45. {py_predicate-0.6 → py_predicate-0.7}/predicate/optimizer/any_optimizer.py +3 -8
  46. {py_predicate-0.6 → py_predicate-0.7}/predicate/optimizer/in_optimizer.py +3 -1
  47. {py_predicate-0.6 → py_predicate-0.7}/predicate/optimizer/or_optimizer.py +2 -8
  48. {py_predicate-0.6 → py_predicate-0.7}/predicate/optimizer/xor_optimizer.py +2 -4
  49. {py_predicate-0.6 → py_predicate-0.7}/predicate/parser.py +19 -11
  50. {py_predicate-0.6 → py_predicate-0.7}/predicate/predicate.py +26 -70
  51. {py_predicate-0.6 → py_predicate-0.7}/predicate/property_predicate.py +3 -3
  52. {py_predicate-0.6 → py_predicate-0.7}/predicate/range_predicate.py +3 -4
  53. {py_predicate-0.6 → py_predicate-0.7}/predicate/regex_predicate.py +5 -0
  54. {py_predicate-0.6 → py_predicate-0.7}/predicate/root_predicate.py +1 -2
  55. py_predicate-0.7/predicate/set_of_predicate.py +27 -0
  56. {py_predicate-0.6 → py_predicate-0.7}/predicate/standard_predicates.py +11 -6
  57. py_predicate-0.7/predicate/this_predicate.py +33 -0
  58. {py_predicate-0.6 → py_predicate-0.7}/predicate/truth_table.py +14 -4
  59. {py_predicate-0.6 → py_predicate-0.7}/predicate/tuple_of_predicate.py +4 -7
  60. {py_predicate-0.6 → py_predicate-0.7}/pyproject.toml +1 -1
  61. py_predicate-0.6/predicate/optimizer/__init__.py +0 -0
  62. py_predicate-0.6/predicate/set_of_predicate.py +0 -25
  63. py_predicate-0.6/predicate/this_predicate.py +0 -58
  64. {py_predicate-0.6 → py_predicate-0.7}/predicate/constructor/__init__.py +0 -0
  65. {py_predicate-0.6 → py_predicate-0.7}/predicate/explain.py +0 -0
  66. {py_predicate-0.6 → py_predicate-0.7}/predicate/generator/__init__.py +0 -0
  67. {py_predicate-0.6 → py_predicate-0.7}/predicate/ip_address_predicates.py +0 -0
  68. {py_predicate-0.6 → py_predicate-0.7}/predicate/lazy_predicate.py +0 -0
  69. {py_predicate-0.6 → py_predicate-0.7}/predicate/named_predicate.py +0 -0
  70. {py_predicate-0.6/predicate/formatter → py_predicate-0.7/predicate/optimizer}/__init__.py +0 -0
  71. {py_predicate-0.6 → py_predicate-0.7}/predicate/optimizer/not_optimizer.py +0 -0
  72. {py_predicate-0.6 → py_predicate-0.7}/predicate/optimizer/predicate_optimizer.py +0 -0
  73. {py_predicate-0.6 → py_predicate-0.7}/predicate/set_predicates.py +0 -0
  74. {py_predicate-0.6 → py_predicate-0.7}/predicate/str_predicates.py +0 -0
  75. {py_predicate-0.6 → py_predicate-0.7}/predicate/tee_predicate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: py_predicate
3
- Version: 0.6
3
+ Version: 0.7
4
4
  Summary: Module to create composable predicates
5
5
  Author-email: Maurits Rijk <maurits.rijk@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -81,6 +81,21 @@ filtered = [x for x in range(10) if between_2_and_3(x)]
81
81
  Of course this example looks way more complicated than the original version. The point here is that you can build
82
82
  reusable predicates that can be used in multiple locations.
83
83
 
84
+ So lets do just that, reuse our predicate to create a generator. The ``generate_false`` will create an infinite
85
+ series of integers for which the predicate ``between_2_and_3`` is False. The ``generate_true`` will create an
86
+ infinite (well, with lots of duplicates obviously) series of integers for which the predicate is True.
87
+
88
+ ```python
89
+ from predicate import generate_false, generate_true
90
+ from more_itertools import take
91
+
92
+ take(5, generate_true(between_2_and_3))
93
+
94
+ take(5, generate_false(between_2_and_3))
95
+ ```
96
+
97
+ This might be useful for example in unit tests.
98
+
84
99
  # Example 2
85
100
 
86
101
  A unique (?) py-predicate feature is that you can define self referencing predicates.
@@ -38,6 +38,21 @@ filtered = [x for x in range(10) if between_2_and_3(x)]
38
38
  Of course this example looks way more complicated than the original version. The point here is that you can build
39
39
  reusable predicates that can be used in multiple locations.
40
40
 
41
+ So lets do just that, reuse our predicate to create a generator. The ``generate_false`` will create an infinite
42
+ series of integers for which the predicate ``between_2_and_3`` is False. The ``generate_true`` will create an
43
+ infinite (well, with lots of duplicates obviously) series of integers for which the predicate is True.
44
+
45
+ ```python
46
+ from predicate import generate_false, generate_true
47
+ from more_itertools import take
48
+
49
+ take(5, generate_true(between_2_and_3))
50
+
51
+ take(5, generate_false(between_2_and_3))
52
+ ```
53
+
54
+ This might be useful for example in unit tests.
55
+
41
56
  # Example 2
42
57
 
43
58
  A unique (?) py-predicate feature is that you can define self referencing predicates.
@@ -2,37 +2,16 @@
2
2
 
3
3
  __version__ = "0.0.1"
4
4
 
5
- from predicate.all_predicate import AllPredicate
6
- from predicate.any_predicate import AnyPredicate
7
- from predicate.eq_predicate import EqPredicate
5
+ from predicate.always_false_predicate import always_false_p, never_p
6
+ from predicate.always_true_predicate import always_p, always_true_p
8
7
  from predicate.explain import explain
9
- from predicate.formatter.format_dot import to_dot
10
- from predicate.formatter.format_json import to_json
11
- from predicate.ge_predicate import GePredicate
8
+ from predicate.formatter import to_dot, to_json, to_latex
12
9
  from predicate.generator.generate_false import generate_false
13
10
  from predicate.generator.generate_true import generate_true
14
- from predicate.gt_predicate import GtPredicate
15
- from predicate.is_empty_predicate import IsEmptyPredicate, IsNotEmptyPredicate, is_empty_p, is_not_empty_p
16
- from predicate.is_none_predicate import IsNonePredicate
17
- from predicate.is_not_none_predicate import IsNotNonePredicate
18
- from predicate.le_predicate import LePredicate
19
- from predicate.lt_predicate import LtPredicate
20
- from predicate.ne_predicate import NePredicate
11
+ from predicate.is_empty_predicate import is_empty_p, is_not_empty_p
12
+ from predicate.is_lambda_predicate import is_lambda_p, is_lambda_with_signature_p
21
13
  from predicate.optimizer.predicate_optimizer import can_optimize, optimize
22
- from predicate.predicate import (
23
- AlwaysFalsePredicate,
24
- AlwaysTruePredicate,
25
- AndPredicate,
26
- NotPredicate,
27
- OrPredicate,
28
- Predicate,
29
- XorPredicate,
30
- always_false_p,
31
- always_true_p,
32
- )
33
14
  from predicate.set_predicates import (
34
- InPredicate,
35
- NotInPredicate,
36
15
  in_p,
37
16
  is_real_subset_p,
38
17
  is_real_superset_p,
@@ -100,29 +79,9 @@ from predicate.standard_predicates import (
100
79
  )
101
80
 
102
81
  __all__ = [
103
- "AllPredicate",
104
- "AlwaysFalsePredicate",
105
- "AlwaysTruePredicate",
106
- "AndPredicate",
107
- "AnyPredicate",
108
- "EqPredicate",
109
- "GePredicate",
110
- "GtPredicate",
111
- "LePredicate",
112
- "LtPredicate",
113
- "InPredicate",
114
- "IsEmptyPredicate",
115
- "IsNonePredicate",
116
- "IsNotEmptyPredicate",
117
- "IsNotNonePredicate",
118
- "NePredicate",
119
- "NotInPredicate",
120
- "NotPredicate",
121
- "OrPredicate",
122
- "Predicate",
123
- "XorPredicate",
124
82
  "all_p",
125
83
  "always_false_p",
84
+ "always_p",
126
85
  "always_true_p",
127
86
  "any_p",
128
87
  "can_optimize",
@@ -165,6 +124,8 @@ __all__ = [
165
124
  "is_int_p",
166
125
  "is_iterable_of_p",
167
126
  "is_iterable_p",
127
+ "is_lambda_p",
128
+ "is_lambda_with_signature_p",
168
129
  "is_list_of_p",
169
130
  "is_list_p",
170
131
  "is_lower_p",
@@ -192,6 +153,7 @@ __all__ = [
192
153
  "lt_p",
193
154
  "ne_p",
194
155
  "neg_p",
156
+ "never_p",
195
157
  "not_in_p",
196
158
  "optimize",
197
159
  "pos_p",
@@ -201,6 +163,7 @@ __all__ = [
201
163
  "this_p",
202
164
  "to_dot",
203
165
  "to_json",
166
+ "to_latex",
204
167
  "zero_p",
205
168
  ]
206
169
 
@@ -1,8 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Iterable, override
3
3
 
4
- from more_itertools import first
5
-
4
+ from predicate.helpers import first_false
6
5
  from predicate.predicate import Predicate
7
6
 
8
7
 
@@ -15,11 +14,14 @@ class AllPredicate[T](Predicate[T]):
15
14
  def __call__(self, iterable: Iterable[T]) -> bool:
16
15
  return all(self.predicate(x) for x in iterable)
17
16
 
17
+ def __contains__(self, predicate: Predicate[T]) -> bool:
18
+ return predicate in self.predicate
19
+
18
20
  def __repr__(self) -> str:
19
21
  return f"all({repr(self.predicate)})"
20
22
 
21
23
  @override
22
24
  def explain_failure(self, iterable: Iterable[T]) -> dict:
23
- fail = first(item for item in iterable if not self.predicate(item))
25
+ fail = first_false(iterable, self.predicate)
24
26
 
25
- return {"result": False, "reason": f"Item '{fail}' didn't match predicate {repr(self.predicate)}"}
27
+ return {"reason": f"Item '{fail}' didn't match predicate {repr(self.predicate)}"}
@@ -0,0 +1,26 @@
1
+ from dataclasses import dataclass
2
+ from typing import Final, override
3
+
4
+ from predicate.predicate import Predicate
5
+
6
+
7
+ @dataclass
8
+ class AlwaysFalsePredicate(Predicate):
9
+ """A predicate class that models the 'False' predicate."""
10
+
11
+ def __call__(self, *args, **kwargs):
12
+ return False
13
+
14
+ def __repr__(self) -> str:
15
+ return "always_false_p"
16
+
17
+ @override
18
+ def explain_failure(self, *args, **kwargs) -> dict:
19
+ return {"reason": "Always returns False"}
20
+
21
+
22
+ always_false_p: Final[AlwaysFalsePredicate] = AlwaysFalsePredicate()
23
+ """Predicate that always evaluates to False."""
24
+
25
+ never_p = always_false_p
26
+ """Synonym for always_false_p."""
@@ -0,0 +1,22 @@
1
+ from dataclasses import dataclass
2
+ from typing import Final
3
+
4
+ from predicate.predicate import Predicate
5
+
6
+
7
+ @dataclass
8
+ class AlwaysTruePredicate(Predicate):
9
+ """A predicate class that models the 'True' predicate."""
10
+
11
+ def __call__(self, *args, **kwargs):
12
+ return True
13
+
14
+ def __repr__(self) -> str:
15
+ return "always_true_p"
16
+
17
+
18
+ always_true_p: Final[AlwaysTruePredicate] = AlwaysTruePredicate()
19
+ """Predicate that always evaluates to True."""
20
+
21
+ always_p = always_true_p
22
+ """Synonym for always_true_p."""
@@ -13,9 +13,12 @@ class AnyPredicate[T](Predicate[T]):
13
13
  def __call__(self, iterable: Iterable[T]) -> bool:
14
14
  return any(self.predicate(x) for x in iterable)
15
15
 
16
+ def __contains__(self, predicate: Predicate[T]) -> bool:
17
+ return predicate in self.predicate
18
+
16
19
  def __repr__(self) -> str:
17
20
  return f"any({repr(self.predicate)})"
18
21
 
19
22
  @override
20
23
  def explain_failure(self, iterable: Iterable[T]) -> dict:
21
- return {"result": False, "reason": f"No item matches predicate {repr(self.predicate)}"}
24
+ return {"reason": f"No item matches predicate {repr(self.predicate)}"}
@@ -17,6 +17,9 @@ class CompPredicate[S, T](Predicate[T]):
17
17
  def __repr__(self) -> str:
18
18
  return f"comp_p({repr(self.predicate)})"
19
19
 
20
+ def __contains__(self, predicate: Predicate[T]) -> bool:
21
+ return predicate in self.predicate
22
+
20
23
  @override
21
24
  def explain_failure(self, x: S) -> dict:
22
- return {"result": False, "predicate": self.predicate.explain(x)}
25
+ return {"reason": self.predicate.explain(x)}
@@ -2,8 +2,19 @@ from typing import Iterator
2
2
 
3
3
  from more_itertools import gray_product
4
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
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.predicate import Predicate
7
18
  from predicate.standard_predicates import all_p, is_bool_p, is_dict_p, is_list_p, is_none_p
8
19
 
9
20
  # TODO: this is very much work under construction (pun intended) and not ready for public consumption
@@ -1,6 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Any, override
3
3
 
4
+ from predicate.eq_predicate import EqPredicate
4
5
  from predicate.predicate import Predicate
5
6
 
6
7
 
@@ -11,15 +12,6 @@ class DictOfPredicate[T](Predicate[T]):
11
12
  key_value_predicates: list[tuple[Predicate, Predicate]]
12
13
 
13
14
  def __init__(self, key_value_predicates: list[tuple[Predicate | str, Predicate]]):
14
- def to_key_p(key_p: Predicate | str) -> Predicate:
15
- from predicate.standard_predicates import eq_p
16
-
17
- match key_p:
18
- case str(s):
19
- return eq_p(s)
20
- case _:
21
- return key_p
22
-
23
15
  self.key_value_predicates = [(to_key_p(key_p), value_p) for key_p, value_p in key_value_predicates]
24
16
 
25
17
  def __call__(self, x: Any) -> bool:
@@ -42,10 +34,37 @@ class DictOfPredicate[T](Predicate[T]):
42
34
  return True
43
35
 
44
36
  def __repr__(self) -> str:
45
- # TODO: show predicates
46
- return "is_dict_of_p"
37
+ def to_key_value_str(key_p: Predicate, value_p: Predicate) -> str:
38
+ return f"({repr(from_key_p(key_p))}, {repr(value_p)})"
39
+
40
+ key_value_predicates = ", ".join(
41
+ to_key_value_str(key_p, value_p) for key_p, value_p in self.key_value_predicates
42
+ )
43
+
44
+ return f"is_dict_of_p({key_value_predicates})"
47
45
 
48
46
  @override
49
47
  def explain_failure(self, x: Any) -> dict:
50
- # TODO: finish
51
- return {"result": False, "key_value_predicates": []}
48
+ match x:
49
+ case dict():
50
+ return {"key_value_predicates": []}
51
+ case _:
52
+ return {"reason": f"{x} is not an instance of a dict"}
53
+
54
+
55
+ def to_key_p(key_p: Predicate | str) -> Predicate:
56
+ from predicate.standard_predicates import eq_p
57
+
58
+ match key_p:
59
+ case str(s):
60
+ return eq_p(s)
61
+ case _:
62
+ return key_p
63
+
64
+
65
+ def from_key_p(key_p: Predicate) -> Predicate | str:
66
+ match key_p:
67
+ case EqPredicate(v):
68
+ return v
69
+ case _:
70
+ return key_p
@@ -18,4 +18,4 @@ class EqPredicate[T](Predicate[T]):
18
18
 
19
19
  @override
20
20
  def explain_failure(self, x: T) -> dict:
21
- return {"result": False, "reason": f"{x} is not equal to {self.v}"}
21
+ return {"reason": f"{x} is not equal to {self.v}"}
@@ -14,8 +14,8 @@ class FnPredicate[T](Predicate[T]):
14
14
  return self.predicate_fn(x)
15
15
 
16
16
  def __repr__(self) -> str:
17
- return "fn_p"
17
+ return f"fn_p(predicate_fn={self.predicate_fn.__name__})"
18
18
 
19
19
  @override
20
20
  def explain_failure(self, x: T) -> dict:
21
- return {"result": False, "reason": f"Function returned False for value {x}"}
21
+ return {"reason": f"Function returned False for value {x}"}
@@ -0,0 +1,9 @@
1
+ from predicate.formatter.format_dot import to_dot
2
+ from predicate.formatter.format_json import to_json
3
+ from predicate.formatter.format_latex import to_latex
4
+
5
+ __all__ = [
6
+ "to_dot",
7
+ "to_json",
8
+ "to_latex",
9
+ ]
@@ -6,15 +6,21 @@ import graphviz # type: ignore
6
6
  from more_itertools import first
7
7
 
8
8
  from predicate.all_predicate import AllPredicate
9
+ from predicate.always_false_predicate import AlwaysFalsePredicate
10
+ from predicate.always_true_predicate import AlwaysTruePredicate
9
11
  from predicate.any_predicate import AnyPredicate
10
12
  from predicate.comp_predicate import CompPredicate
11
13
  from predicate.dict_of_predicate import DictOfPredicate
12
14
  from predicate.eq_predicate import EqPredicate
13
15
  from predicate.fn_predicate import FnPredicate
16
+ from predicate.formatter.helpers import set_to_str
14
17
  from predicate.ge_predicate import GePredicate
15
18
  from predicate.gt_predicate import GtPredicate
19
+ from predicate.implies import Implies
20
+ from predicate.is_falsy_predicate import IsFalsyPredicate
16
21
  from predicate.is_instance_predicate import IsInstancePredicate
17
22
  from predicate.is_none_predicate import IsNonePredicate
23
+ from predicate.is_truthy_predicate import IsTruthyPredicate
18
24
  from predicate.lazy_predicate import LazyPredicate, find_predicate_by_ref
19
25
  from predicate.le_predicate import LePredicate
20
26
  from predicate.lt_predicate import LtPredicate
@@ -22,11 +28,7 @@ from predicate.named_predicate import NamedPredicate
22
28
  from predicate.ne_predicate import NePredicate
23
29
  from predicate.optimizer.predicate_optimizer import optimize
24
30
  from predicate.predicate import (
25
- AlwaysFalsePredicate,
26
- AlwaysTruePredicate,
27
31
  AndPredicate,
28
- IsFalsyPredicate,
29
- IsTruthyPredicate,
30
32
  NotPredicate,
31
33
  OrPredicate,
32
34
  Predicate,
@@ -69,12 +71,6 @@ def to_dot(predicate: Predicate, predicate_string: str | None = None, show_optim
69
71
  return dot
70
72
 
71
73
 
72
- def set_to_str(v: set) -> str:
73
- # TODO: truncate if too many items.
74
- items = ", ".join(str(item) for item in v)
75
- return f"{{{items}}}"
76
-
77
-
78
74
  def render(dot, predicate: Predicate, node_nr: count):
79
75
  node_predicate_mapping: dict[str, Predicate] = {}
80
76
 
@@ -147,6 +143,8 @@ def render(dot, predicate: Predicate, node_nr: count):
147
143
  dot.edge(kv, to_value(key), label="key")
148
144
  dot.edge(kv, to_value(value), label="value")
149
145
  return node
146
+ case Implies(left, right):
147
+ return add_node_left_right("implies", label="=>", left=left, right=right)
150
148
  case IsInstancePredicate(klass):
151
149
  name = klass[0].__name__ # type: ignore
152
150
  return add_node("instance", label=f"is_{name}_p")
@@ -209,15 +207,12 @@ def render_lazy_references(dot, node_predicate_mapping) -> None:
209
207
 
210
208
  for node, predicate in node_predicate_mapping.items():
211
209
  match predicate:
212
- case LazyPredicate():
213
- if reference := find_predicate_by_ref(frame, predicate.ref):
214
- add_dashed_line(node, reference)
215
- case RootPredicate():
216
- if root := find_root_predicate(frame, predicate):
217
- add_dashed_line(node, root)
218
- case ThisPredicate():
219
- if this := find_this_predicate(frame, predicate):
220
- add_dashed_line(node, this)
210
+ case LazyPredicate() if reference := find_predicate_by_ref(frame, predicate.ref):
211
+ add_dashed_line(node, reference)
212
+ case RootPredicate() if root := find_root_predicate(frame, predicate):
213
+ add_dashed_line(node, root)
214
+ case ThisPredicate() if this := find_this_predicate(frame, predicate):
215
+ add_dashed_line(node, this)
221
216
 
222
217
 
223
218
  def render_original(dot, predicate: Predicate, node_nr) -> None:
@@ -1,16 +1,16 @@
1
1
  from typing import Any
2
2
 
3
3
  from predicate.all_predicate import AllPredicate
4
+ from predicate.always_false_predicate import AlwaysFalsePredicate
5
+ from predicate.always_true_predicate import AlwaysTruePredicate
4
6
  from predicate.any_predicate import AnyPredicate
5
7
  from predicate.fn_predicate import FnPredicate
8
+ from predicate.is_falsy_predicate import IsFalsyPredicate
9
+ from predicate.is_truthy_predicate import IsTruthyPredicate
6
10
  from predicate.named_predicate import NamedPredicate
7
11
  from predicate.ne_predicate import NePredicate
8
12
  from predicate.predicate import (
9
- AlwaysFalsePredicate,
10
- AlwaysTruePredicate,
11
13
  AndPredicate,
12
- IsFalsyPredicate,
13
- IsTruthyPredicate,
14
14
  NotPredicate,
15
15
  OrPredicate,
16
16
  Predicate,
@@ -0,0 +1,86 @@
1
+ from predicate.all_predicate import AllPredicate
2
+ from predicate.always_false_predicate import AlwaysFalsePredicate
3
+ from predicate.always_true_predicate import AlwaysTruePredicate
4
+ from predicate.any_predicate import AnyPredicate
5
+ from predicate.eq_predicate import EqPredicate
6
+ from predicate.ge_predicate import GePredicate
7
+ from predicate.gt_predicate import GtPredicate
8
+ from predicate.implies_predicate import ImpliesPredicate
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 (
13
+ AndPredicate,
14
+ NotPredicate,
15
+ OrPredicate,
16
+ Predicate,
17
+ XorPredicate,
18
+ )
19
+ from predicate.range_predicate import GeLePredicate, GeLtPredicate, GtLePredicate, GtLtPredicate
20
+ from predicate.set_predicates import (
21
+ InPredicate,
22
+ IsRealSubsetPredicate,
23
+ IsRealSupersetPredicate,
24
+ IsSubsetPredicate,
25
+ IsSupersetPredicate,
26
+ )
27
+
28
+
29
+ def set_to_latex_set(v: set) -> str:
30
+ items = ", ".join(str(item) for item in v)
31
+ return f"\\{{{items}\\}}"
32
+
33
+
34
+ def to_latex(predicate: Predicate) -> str:
35
+ """Format predicate as LaTeX."""
36
+ match predicate:
37
+ case AllPredicate(all_predicate):
38
+ return f"\\forall x \\in S, {to_latex(all_predicate)}"
39
+ case AlwaysFalsePredicate():
40
+ return "False"
41
+ case AlwaysTruePredicate():
42
+ return "True"
43
+ case AndPredicate(left, right):
44
+ return f"{to_latex(left)} \\wedge {to_latex(right)}"
45
+ case AnyPredicate(all_predicate):
46
+ return f"\\exists x \\in S, {to_latex(all_predicate)}"
47
+ case EqPredicate(v):
48
+ return f"x = {v}"
49
+ case GePredicate(v):
50
+ return f"x \\ge {v}"
51
+ case GeLePredicate(lower, upper):
52
+ return f"{lower} \\le x \\le {upper}"
53
+ case GeLtPredicate(lower, upper):
54
+ return f"{lower} \\le x \\lt {upper}"
55
+ case GtLePredicate(lower, upper):
56
+ return f"{lower} \\lt x \\le {upper}"
57
+ case GtLtPredicate(lower, upper):
58
+ return f"{lower} \\lt x \\lt {upper}"
59
+ case GtPredicate(v):
60
+ return f"x \\gt {v}"
61
+ case ImpliesPredicate(p):
62
+ return f"p \\implies {to_latex(p)}"
63
+ case InPredicate(v):
64
+ return f"x \\in {set_to_latex_set(v)}"
65
+ case IsRealSubsetPredicate(v):
66
+ return f"x \\subseteq {set_to_latex_set(v)}"
67
+ case IsSubsetPredicate(v):
68
+ return f"x \\subset {set_to_latex_set(v)}"
69
+ case IsRealSupersetPredicate(v):
70
+ return f"x \\supseteq {set_to_latex_set(v)}"
71
+ case IsSupersetPredicate(v):
72
+ return f"x \\supset {set_to_latex_set(v)}"
73
+ case LePredicate(v):
74
+ return f"x \\le {v}"
75
+ case LtPredicate(v):
76
+ return f"x \\lt {v}"
77
+ case NePredicate(v):
78
+ return f"x \\neq {v}"
79
+ case NotPredicate(child):
80
+ return f"\\neg {to_latex(child)}"
81
+ case OrPredicate(left, right):
82
+ return f"{to_latex(left)} \\vee {to_latex(right)}"
83
+ case XorPredicate(left, right):
84
+ return f"{to_latex(left)} \\oplus {to_latex(right)}"
85
+ case _:
86
+ raise ValueError(f"Unknown predicate type {predicate}")
@@ -0,0 +1,4 @@
1
+ def set_to_str(v: set) -> str:
2
+ # TODO: truncate if too many items.
3
+ items = ", ".join(str(item) for item in v)
4
+ return f"{{{items}}}"
@@ -18,4 +18,4 @@ class GePredicate[T](Predicate[T]):
18
18
 
19
19
  @override
20
20
  def explain_failure(self, x: T) -> dict:
21
- return {"result": False, "reason": f"{x} is not greater or equal to {self.v}"}
21
+ return {"reason": f"{x} is not greater or equal to {self.v}"}