-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbase.py
177 lines (120 loc) · 4.74 KB
/
base.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
"""base conjecture classes."""
import collections.abc
import typing
Proof = collections.abc.Callable[[object], bool]
class Conjecture:
"""
A conjecture describing another object.
Conjectures can be used to describe an object to which they are to be compared
against. They assert equality when the proof function resolves truly.
>>> assert 5 == conjecture.greater_than(0) & conjecture.less_than(10)
There are many helpful conjecture factories with their proofs already defined.
"""
# pylint: disable=eq-without-hash
def __init__(self, proof: typing.Optional[Proof] = None) -> None:
"""
Create a conjecture.
:param proof: a callback that asserts some small fact of the passed object
"""
self._proof = proof
def resolve(self, value: object) -> bool:
"""
Resolve conjecture.
This is an abstract method can either be overwritten in a subclass or by
providing a proof method to the constructor.
:param value: the value the conjecture is evaluated against
:return: whether the conjecture resolved truly
:raises NotImplementedError: when no proof specified
"""
if not self._proof:
raise NotImplementedError()
return self._proof(value)
def __eq__(self, other: object) -> bool:
"""
Resolve conjecture via equality.
A conjecture can be resolved via `==` or `!=` comparison operators.
:param other: the value the conjecture is evaluated against
:return: whether the conjecture resolved truly
"""
return self.resolve(other)
def __invert__(self) -> "Conjecture":
"""
Invert conjecture.
Invert the resolution of a conjecture
:return: the inverse conjecture
"""
return Conjecture(lambda value: not self.resolve(value))
def __or__(self, other: object) -> "Conjecture":
"""
Combine using any of.
:param other: another conjecture
:return: a conjecture that either of the combined conjectures will
resolve truly
:raises ValueError: when other is not a conjecture
"""
if not isinstance(other, Conjecture):
raise ValueError(f"Conjecture cannot be combined with {other!r}")
return AnyOfConjecture((self, other))
def __and__(self, other: object) -> "Conjecture":
"""
Combine using all of.
:param other: another conjecture
:return: a conjecture that both of the combined conjectures will resolve truly
:raises ValueError: when other is not a conjecture
"""
if not isinstance(other, Conjecture):
raise ValueError(f"Conjecture cannot be combined with {other!r}")
return AllOfConjecture((self, other))
class AnyOfConjecture(Conjecture):
"""
Any of Conjecture.
An any of conjecture will resolve truly if any of the passed conjectures
resolve truly themselves.
"""
# pylint: disable=too-few-public-methods
def __init__(self, conjectures: collections.abc.Iterable[Conjecture]) -> None:
"""
Create a combined conjecture.
:param conjectures: a collection of conjectures
"""
super().__init__()
self.conjectures = conjectures
def resolve(self, value: object) -> bool:
"""
Resolve conjecture.
This conjecture will resolve truly if any of the conjectures it was
created with resolve true.
:param value: the value the conjecture is evaluated against
:return: whether the conjecture resolved truly
"""
for other in self.conjectures:
if other.resolve(value):
return True
return False
class AllOfConjecture(Conjecture):
"""
All of Conjecture.
An all of conjecture will resolve truly only when all of the passed conjectures
resolve truly themselves.
:param conjectures: a tuple of conjectures
"""
# pylint: disable=too-few-public-methods
def __init__(self, conjectures: collections.abc.Iterable[Conjecture]) -> None:
"""
Create a combined conjecture.
:param conjectures: a collection of conjectures
"""
super().__init__()
self.conjectures = conjectures
def resolve(self, value: object) -> bool:
"""
Resolve conjecture.
This conjecture will resolve truly only if all of the conjectures it was
created with resolve true.
:param value: the value the conjecture is evaluated against
:return: whether the conjecture resolved truly
"""
for other in self.conjectures:
if not other.resolve(value):
return False
return True