Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/hypothesis/errors.py: 65%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

93 statements  

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 collections.abc import Mapping 

12from datetime import timedelta 

13from typing import TYPE_CHECKING, Any, Literal 

14 

15from hypothesis.internal.compat import ExceptionGroup 

16 

17if TYPE_CHECKING: 

18 from hypothesis.internal.conjecture.choice import ChoiceConstraintsT 

19else: 

20 ChoiceConstraintsT = Mapping 

21 

22 

23class HypothesisException(Exception): 

24 """Generic parent class for exceptions thrown by Hypothesis.""" 

25 

26 

27class _Trimmable(HypothesisException): 

28 """Hypothesis can trim these tracebacks even if they're raised internally.""" 

29 

30 

31class UnsatisfiedAssumption(HypothesisException): 

32 """An internal error raised by assume. 

33 

34 If you're seeing this error something has gone wrong. 

35 """ 

36 

37 def __init__(self, reason: str | None = None) -> None: 

38 self.reason = reason 

39 

40 

41class NoSuchExample(HypothesisException): 

42 """The condition we have been asked to satisfy appears to be always false. 

43 

44 This does not guarantee that no example exists, only that we were 

45 unable to find one. 

46 """ 

47 

48 def __init__(self, condition_string: str, extra: str = "") -> None: 

49 super().__init__(f"No examples found of condition {condition_string}{extra}") 

50 

51 

52class Unsatisfiable(_Trimmable): 

53 """We ran out of test cases before we could find enough which satisfy the 

54 assumptions of this hypothesis. 

55 

56 This could be because the function is using |assume| in a way that is 

57 too hard to satisfy. If so, try writing a custom strategy or using a 

58 better starting point (e.g if you are requiring a list has unique 

59 values you could instead filter out all duplicate values from the list) 

60 """ 

61 

62 

63class ChoiceTooLarge(HypothesisException): 

64 """An internal error raised by choice_from_index.""" 

65 

66 

67class Flaky(_Trimmable): 

68 """ 

69 Base class for indeterministic failures. Usually one of the more 

70 specific subclasses (|FlakyFailure| or |FlakyStrategyDefinition|) is raised. 

71 

72 .. seealso:: 

73 

74 See also the :doc:`flaky failures tutorial </tutorial/flaky>`. 

75 """ 

76 

77 

78class FlakyReplay(Flaky): 

79 """Internal error raised by the conjecture engine if flaky failures are 

80 detected during replay. 

81 

82 Carries information allowing the runner to reconstruct the flakiness as 

83 a FlakyFailure exception group for final presentation. 

84 """ 

85 

86 def __init__(self, reason, interesting_origins=None): 

87 super().__init__(reason) 

88 self.reason = reason 

89 self._interesting_origins = interesting_origins 

90 

91 

92def _render_constraints(show: Mapping[str, object], other: Mapping[str, object]) -> str: 

93 assert show.keys() == other.keys() 

94 return ", ".join( 

95 f"{k}={'...' if v == other[k] else repr(v)}" for k, v in show.items() 

96 ) 

97 

98 

99class FlakyStrategyDefinition(Flaky): 

100 """ 

101 This function appears to cause inconsistent data generation. 

102 

103 Common causes for this problem are: 

104 1. The strategy depends on external state. e.g. it uses an external 

105 random number generator. Try to make a version that passes all the 

106 relevant state in from Hypothesis. 

107 

108 .. seealso:: 

109 

110 See also the :doc:`flaky failures tutorial </tutorial/flaky>`. 

111 """ 

112 

113 _BASE_MESSAGE = ( 

114 "Inconsistent data generation! Data generation behaved differently " 

115 "between test cases. Is your data generation depending on external " 

116 "state?" 

117 ) 

118 

119 @classmethod 

120 def with_detail(cls, detail: str) -> "FlakyStrategyDefinition": 

121 return cls(f"{cls._BASE_MESSAGE}\n\n{detail}") 

122 

123 @classmethod 

124 def from_mismatch( 

125 cls, 

126 expected_type: str, 

127 expected_constraints: ChoiceConstraintsT, 

128 actual_type: str, 

129 actual_constraints: ChoiceConstraintsT, 

130 ) -> "FlakyStrategyDefinition": 

131 if actual_type != expected_type: 

132 detail = ( 

133 "The second test case drew a different type of value than the first.\n" 

134 f" first: {expected_type}\n" 

135 f" second: {actual_type}\n" 

136 ) 

137 else: 

138 detail = ( 

139 f"The second test case drew type {actual_type} with different constraints " 

140 "than the first.\n" 

141 f" first: {_render_constraints(expected_constraints, actual_constraints)}\n" 

142 f" second: {_render_constraints(actual_constraints, expected_constraints)}\n" 

143 ) 

144 return cls.with_detail(detail) 

145 

146 

147class _WrappedBaseException(Exception): 

148 """Used internally for wrapping BaseExceptions as components of FlakyFailure.""" 

149 

150 

151class FlakyFailure(ExceptionGroup, Flaky): 

152 """ 

153 This function appears to fail non-deterministically: We have seen it 

154 fail when passed this value at least once, but a subsequent invocation 

155 did not fail, or caused a distinct error. 

156 

157 Common causes for this problem are: 

158 1. The function depends on external state. e.g. it uses an external 

159 random number generator. Try to make a version that passes all the 

160 relevant state in from Hypothesis. 

161 2. The function is suffering from too much recursion and its failure 

162 depends sensitively on where it's been called from. 

163 3. The function is timing sensitive and can fail or pass depending on 

164 how long it takes. Try breaking it up into smaller functions which 

165 don't do that and testing those instead. 

166 

167 .. seealso:: 

168 

169 See also the :doc:`flaky failures tutorial </tutorial/flaky>`. 

170 """ 

171 

172 def __new__(cls, msg, group): 

173 # The Exception mixin forces this an ExceptionGroup (only accepting 

174 # Exceptions, not BaseException). Usually BaseException is raised 

175 # directly and will hence not be part of a FlakyFailure, but I'm not 

176 # sure this assumption holds everywhere. So wrap any BaseExceptions. 

177 group = list(group) 

178 for i, exc in enumerate(group): 

179 if not isinstance(exc, Exception): 

180 err = _WrappedBaseException() 

181 err.__cause__ = err.__context__ = exc 

182 group[i] = err 

183 return ExceptionGroup.__new__(cls, msg, group) 

184 

185 # defining `derive` is required for `split` to return an instance of FlakyFailure 

186 # instead of ExceptionGroup. See https://github.com/python/cpython/issues/119287 

187 # and https://docs.python.org/3/library/exceptions.html#BaseExceptionGroup.derive 

188 def derive(self, excs): 

189 return type(self)(self.message, excs) 

190 

191 

192class FlakyBackendFailure(FlakyFailure): 

193 """ 

194 A failure was reported by an |alternative backend|, 

195 but this failure did not reproduce when replayed under the Hypothesis backend. 

196 

197 When an alternative backend reports a failure, Hypothesis first replays it 

198 under the standard Hypothesis backend to check for flakiness. If the failure 

199 does not reproduce, Hypothesis raises this exception. 

200 """ 

201 

202 

203class InvalidArgument(_Trimmable, TypeError): 

204 """Used to indicate that the arguments to a Hypothesis function were in 

205 some manner incorrect.""" 

206 

207 

208class ResolutionFailed(InvalidArgument): 

209 """Hypothesis had to resolve a type to a strategy, but this failed. 

210 

211 Type inference is best-effort, so this only happens when an 

212 annotation exists but could not be resolved for a required argument 

213 to the target of ``builds()``, or where the user passed ``...``. 

214 """ 

215 

216 

217class InvalidState(HypothesisException): 

218 """The system is not in a state where you were allowed to do that.""" 

219 

220 

221class InvalidDefinition(_Trimmable, TypeError): 

222 """Used to indicate that a class definition was not well put together and 

223 has something wrong with it.""" 

224 

225 

226class HypothesisWarning(HypothesisException, Warning): 

227 """A generic warning issued by Hypothesis.""" 

228 

229 

230class FailedHealthCheck(_Trimmable): 

231 """Raised when a test fails a health check. See |HealthCheck|.""" 

232 

233 

234class NonInteractiveExampleWarning(HypothesisWarning): 

235 """ 

236 Emitted when |.example| is used outside of interactive use. 

237 

238 |.example| is intended for exploratory and interactive work, not to be run as 

239 part of a test suite. 

240 """ 

241 

242 

243class HypothesisDeprecationWarning(HypothesisWarning, FutureWarning): 

244 """A deprecation warning issued by Hypothesis. 

245 

246 Actually inherits from FutureWarning, because DeprecationWarning is 

247 hidden by the default warnings filter. 

248 

249 You can configure the :mod:`python:warnings` module to handle these 

250 warnings differently to others, either turning them into errors or 

251 suppressing them entirely. Obviously we would prefer the former! 

252 """ 

253 

254 

255class HypothesisSideeffectWarning(HypothesisWarning): 

256 """A warning issued by Hypothesis when it sees actions that are 

257 discouraged at import or initialization time because they are 

258 slow or have user-visible side effects. 

259 """ 

260 

261 

262class Frozen(HypothesisException): 

263 """Raised when a mutation method has been called on a ConjectureData object 

264 after freeze() has been called.""" 

265 

266 

267def __getattr__(name: str) -> Any: 

268 if name == "MultipleFailures": 

269 from hypothesis.internal.compat import BaseExceptionGroup 

270 from hypothesis.utils.deprecation import note_deprecation 

271 

272 note_deprecation( 

273 "MultipleFailures is deprecated; use the builtin `BaseExceptionGroup` type " 

274 "instead, or `exceptiongroup.BaseExceptionGroup` before Python 3.11", 

275 since="2022-08-02", 

276 has_codemod=False, # This would be a great PR though! 

277 stacklevel=1, 

278 ) 

279 return BaseExceptionGroup 

280 

281 raise AttributeError(f"Module 'hypothesis.errors' has no attribute {name}") 

282 

283 

284class DeadlineExceeded(_Trimmable): 

285 """ 

286 Raised when an input takes too long to run, relative to the |settings.deadline| 

287 setting. 

288 """ 

289 

290 def __init__(self, runtime: timedelta, deadline: timedelta) -> None: 

291 super().__init__( 

292 f"Test took {runtime.total_seconds() * 1000:.2f}ms, which exceeds " 

293 f"the deadline of {deadline.total_seconds() * 1000:.2f}ms. If you " 

294 "expect test cases to take this long, you can use @settings(deadline=...) " 

295 "to either set a higher deadline, or to disable it with deadline=None." 

296 ) 

297 self.runtime = runtime 

298 self.deadline = deadline 

299 

300 def __reduce__( 

301 self, 

302 ) -> tuple[type["DeadlineExceeded"], tuple[timedelta, timedelta]]: 

303 return (type(self), (self.runtime, self.deadline)) 

304 

305 

306class StopTest(BaseException): 

307 """Raised when a test should stop running and return control to 

308 the Hypothesis engine, which should then continue normally. 

309 """ 

310 

311 def __init__(self, testcounter: int) -> None: 

312 super().__init__(repr(testcounter)) 

313 self.testcounter = testcounter 

314 

315 

316class DidNotReproduce(HypothesisException): 

317 pass 

318 

319 

320class Found(HypothesisException): 

321 """Signal that the example matches condition. Internal use only.""" 

322 

323 

324class RewindRecursive(Exception): 

325 """Signal that the type inference should be rewound due to recursive types. Internal use only.""" 

326 

327 def __init__(self, target: object) -> None: 

328 self.target = target 

329 

330 

331class SmallSearchSpaceWarning(HypothesisWarning): 

332 """Indicates that an inferred strategy does not span the search space 

333 in a meaningful way, for example by only creating default instances.""" 

334 

335 

336CannotProceedScopeT = Literal["verified", "exhausted", "discard_test_case", "other"] 

337_valid_cannot_proceed_scopes = CannotProceedScopeT.__args__ # type: ignore 

338 

339 

340class BackendCannotProceed(HypothesisException): 

341 """ 

342 Raised by alternative backends when a |PrimitiveProvider| cannot proceed. 

343 This is expected to occur inside one of the ``.draw_*()`` methods, or for 

344 symbolic execution perhaps in |PrimitiveProvider.realize|. 

345 

346 The optional ``scope`` argument can enable smarter integration: 

347 

348 verified: 

349 Do not request further |test cases| from this backend. We *may* 

350 generate more test cases with other backends; if one fails then 

351 Hypothesis will report unsound verification in the backend too. 

352 

353 exhausted: 

354 Do not request further test cases from this backend; finish testing 

355 with test cases generated with the default backend. Common if e.g. 

356 native code blocks symbolic reasoning very early. 

357 

358 discard_test_case: 

359 This particular test case could not be converted to concrete values; 

360 skip any further processing and continue with another test case from 

361 this backend. 

362 """ 

363 

364 def __init__(self, scope: CannotProceedScopeT = "other", /) -> None: 

365 if scope not in _valid_cannot_proceed_scopes: 

366 raise InvalidArgument( 

367 f"Got scope={scope}, but expected one of " 

368 f"{_valid_cannot_proceed_scopes!r}" 

369 ) 

370 self.scope = scope