1# This file is part of Hypothesis, which may be found at
2# https://github.com/HypothesisWorks/hypothesis/
3#
4# Copyright the Hypothesis Authors.
5# Individual contributors are listed in AUTHORS.rst and the git log.
6#
7# This Source Code Form is subject to the terms of the Mozilla Public License,
8# v. 2.0. If a copy of the MPL was not distributed with this file, You can
9# obtain one at https://mozilla.org/MPL/2.0/.
10
11from datetime import timedelta
12from typing import Any, Literal, Optional
13
14from hypothesis.internal.compat import ExceptionGroup
15
16
17class HypothesisException(Exception):
18 """Generic parent class for exceptions thrown by Hypothesis."""
19
20
21class _Trimmable(HypothesisException):
22 """Hypothesis can trim these tracebacks even if they're raised internally."""
23
24
25class UnsatisfiedAssumption(HypothesisException):
26 """An internal error raised by assume.
27
28 If you're seeing this error something has gone wrong.
29 """
30
31 def __init__(self, reason: Optional[str] = None) -> None:
32 self.reason = reason
33
34
35class NoSuchExample(HypothesisException):
36 """The condition we have been asked to satisfy appears to be always false.
37
38 This does not guarantee that no example exists, only that we were
39 unable to find one.
40 """
41
42 def __init__(self, condition_string: str, extra: str = "") -> None:
43 super().__init__(f"No examples found of condition {condition_string}{extra}")
44
45
46class Unsatisfiable(_Trimmable):
47 """We ran out of time or examples before we could find enough examples
48 which satisfy the assumptions of this hypothesis.
49
50 This could be because the function is too slow. If so, try upping
51 the timeout. It could also be because the function is using assume
52 in a way that is too hard to satisfy. If so, try writing a custom
53 strategy or using a better starting point (e.g if you are requiring
54 a list has unique values you could instead filter out all duplicate
55 values from the list)
56 """
57
58
59class ChoiceTooLarge(HypothesisException):
60 """An internal error raised by choice_from_index."""
61
62
63class Flaky(_Trimmable):
64 """
65 Base class for indeterministic failures. Usually one of the more
66 specific subclasses (|FlakyFailure| or |FlakyStrategyDefinition|) is raised.
67
68 .. seealso::
69
70 See also the :doc:`flaky failures tutorial </tutorial/flaky>`.
71 """
72
73
74class FlakyReplay(Flaky):
75 """Internal error raised by the conjecture engine if flaky failures are
76 detected during replay.
77
78 Carries information allowing the runner to reconstruct the flakiness as
79 a FlakyFailure exception group for final presentation.
80 """
81
82 def __init__(self, reason, interesting_origins=None):
83 super().__init__(reason)
84 self.reason = reason
85 self._interesting_origins = interesting_origins
86
87
88class FlakyStrategyDefinition(Flaky):
89 """
90 This function appears to cause inconsistent data generation.
91
92 Common causes for this problem are:
93 1. The strategy depends on external state. e.g. it uses an external
94 random number generator. Try to make a version that passes all the
95 relevant state in from Hypothesis.
96
97 .. seealso::
98
99 See also the :doc:`flaky failures tutorial </tutorial/flaky>`.
100 """
101
102
103class _WrappedBaseException(Exception):
104 """Used internally for wrapping BaseExceptions as components of FlakyFailure."""
105
106
107class FlakyFailure(ExceptionGroup, Flaky):
108 """
109 This function appears to fail non-deterministically: We have seen it
110 fail when passed this example at least once, but a subsequent invocation
111 did not fail, or caused a distinct error.
112
113 Common causes for this problem are:
114 1. The function depends on external state. e.g. it uses an external
115 random number generator. Try to make a version that passes all the
116 relevant state in from Hypothesis.
117 2. The function is suffering from too much recursion and its failure
118 depends sensitively on where it's been called from.
119 3. The function is timing sensitive and can fail or pass depending on
120 how long it takes. Try breaking it up into smaller functions which
121 don't do that and testing those instead.
122
123 .. seealso::
124
125 See also the :doc:`flaky failures tutorial </tutorial/flaky>`.
126 """
127
128 def __new__(cls, msg, group):
129 # The Exception mixin forces this an ExceptionGroup (only accepting
130 # Exceptions, not BaseException). Usually BaseException is raised
131 # directly and will hence not be part of a FlakyFailure, but I'm not
132 # sure this assumption holds everywhere. So wrap any BaseExceptions.
133 group = list(group)
134 for i, exc in enumerate(group):
135 if not isinstance(exc, Exception):
136 err = _WrappedBaseException()
137 err.__cause__ = err.__context__ = exc
138 group[i] = err
139 return ExceptionGroup.__new__(cls, msg, group)
140
141 # defining `derive` is required for `split` to return an instance of FlakyFailure
142 # instead of ExceptionGroup. See https://github.com/python/cpython/issues/119287
143 # and https://docs.python.org/3/library/exceptions.html#BaseExceptionGroup.derive
144 def derive(self, excs):
145 return type(self)(self.message, excs)
146
147
148class FlakyBackendFailure(FlakyFailure):
149 """
150 A failure was reported by an |alternative backend|,
151 but this failure did not reproduce when replayed under the Hypothesis backend.
152
153 When an alternative backend reports a failure, Hypothesis first replays it
154 under the standard Hypothesis backend to check for flakiness. If the failure
155 does not reproduce, Hypothesis raises this exception.
156 """
157
158
159class InvalidArgument(_Trimmable, TypeError):
160 """Used to indicate that the arguments to a Hypothesis function were in
161 some manner incorrect."""
162
163
164class ResolutionFailed(InvalidArgument):
165 """Hypothesis had to resolve a type to a strategy, but this failed.
166
167 Type inference is best-effort, so this only happens when an
168 annotation exists but could not be resolved for a required argument
169 to the target of ``builds()``, or where the user passed ``...``.
170 """
171
172
173class InvalidState(HypothesisException):
174 """The system is not in a state where you were allowed to do that."""
175
176
177class InvalidDefinition(_Trimmable, TypeError):
178 """Used to indicate that a class definition was not well put together and
179 has something wrong with it."""
180
181
182class HypothesisWarning(HypothesisException, Warning):
183 """A generic warning issued by Hypothesis."""
184
185
186class FailedHealthCheck(_Trimmable):
187 """Raised when a test fails a health check. See |HealthCheck|."""
188
189
190class NonInteractiveExampleWarning(HypothesisWarning):
191 """SearchStrategy.example() is designed for interactive use,
192 but should never be used in the body of a test.
193 """
194
195
196class HypothesisDeprecationWarning(HypothesisWarning, FutureWarning):
197 """A deprecation warning issued by Hypothesis.
198
199 Actually inherits from FutureWarning, because DeprecationWarning is
200 hidden by the default warnings filter.
201
202 You can configure the :mod:`python:warnings` module to handle these
203 warnings differently to others, either turning them into errors or
204 suppressing them entirely. Obviously we would prefer the former!
205 """
206
207
208class HypothesisSideeffectWarning(HypothesisWarning):
209 """A warning issued by Hypothesis when it sees actions that are
210 discouraged at import or initialization time because they are
211 slow or have user-visible side effects.
212 """
213
214
215class Frozen(HypothesisException):
216 """Raised when a mutation method has been called on a ConjectureData object
217 after freeze() has been called."""
218
219
220def __getattr__(name: str) -> Any:
221 if name == "MultipleFailures":
222 from hypothesis._settings import note_deprecation
223 from hypothesis.internal.compat import BaseExceptionGroup
224
225 note_deprecation(
226 "MultipleFailures is deprecated; use the builtin `BaseExceptionGroup` type "
227 "instead, or `exceptiongroup.BaseExceptionGroup` before Python 3.11",
228 since="2022-08-02",
229 has_codemod=False, # This would be a great PR though!
230 stacklevel=1,
231 )
232 return BaseExceptionGroup
233
234 raise AttributeError(f"Module 'hypothesis.errors' has no attribute {name}")
235
236
237class DeadlineExceeded(_Trimmable):
238 """
239 Raised when an input takes too long to run, relative to the |settings.deadline|
240 setting.
241 """
242
243 def __init__(self, runtime: timedelta, deadline: timedelta) -> None:
244 super().__init__(
245 f"Test took {runtime.total_seconds() * 1000:.2f}ms, which exceeds "
246 f"the deadline of {deadline.total_seconds() * 1000:.2f}ms"
247 )
248 self.runtime = runtime
249 self.deadline = deadline
250
251 def __reduce__(
252 self,
253 ) -> tuple[type["DeadlineExceeded"], tuple[timedelta, timedelta]]:
254 return (type(self), (self.runtime, self.deadline))
255
256
257class StopTest(BaseException):
258 """Raised when a test should stop running and return control to
259 the Hypothesis engine, which should then continue normally.
260 """
261
262 def __init__(self, testcounter: int) -> None:
263 super().__init__(repr(testcounter))
264 self.testcounter = testcounter
265
266
267class DidNotReproduce(HypothesisException):
268 pass
269
270
271class Found(HypothesisException):
272 """Signal that the example matches condition. Internal use only."""
273
274
275class RewindRecursive(Exception):
276 """Signal that the type inference should be rewound due to recursive types. Internal use only."""
277
278 def __init__(self, target: object) -> None:
279 self.target = target
280
281
282class SmallSearchSpaceWarning(HypothesisWarning):
283 """Indicates that an inferred strategy does not span the search space
284 in a meaningful way, for example by only creating default instances."""
285
286
287CannotProceedScopeT = Literal["verified", "exhausted", "discard_test_case", "other"]
288
289
290class BackendCannotProceed(HypothesisException):
291 """
292 Raised by alternative backends when a |PrimitiveProvider| cannot proceed.
293 This is expected to occur inside one of the ``.draw_*()`` methods, or for
294 symbolic execution perhaps in |PrimitiveProvider.realize|.
295
296 The optional ``scope`` argument can enable smarter integration:
297
298 verified:
299 Do not request further test cases from this backend. We *may*
300 generate more test cases with other backends; if one fails then
301 Hypothesis will report unsound verification in the backend too.
302
303 exhausted:
304 Do not request further test cases from this backend; finish testing
305 with test cases generated with the default backend. Common if e.g.
306 native code blocks symbolic reasoning very early.
307
308 discard_test_case:
309 This particular test case could not be converted to concrete values;
310 skip any further processing and continue with another test case from
311 this backend.
312 """
313
314 def __init__(self, scope: CannotProceedScopeT = "other", /) -> None:
315 self.scope = scope