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

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

94 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 

15if TYPE_CHECKING: 

16 from hypothesis.internal.conjecture.choice import ChoiceConstraintsT 

17else: 

18 ChoiceConstraintsT = Mapping 

19 

20 

21class HypothesisException(Exception): 

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

23 

24 

25class _Trimmable(HypothesisException): 

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

27 

28 

29class UnsatisfiedAssumption(HypothesisException): 

30 """An internal error raised by assume. 

31 

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

33 """ 

34 

35 def __init__( 

36 self, reason: str | None = None, *, location: str | None = None 

37 ) -> None: 

38 self.reason = reason 

39 # "filename:lineno" of the failing assume() or reject() call, if known 

40 self.location = location 

41 

42 

43class NoSuchExample(HypothesisException): 

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

45 

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

47 unable to find one. 

48 """ 

49 

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

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

52 

53 

54class Unsatisfiable(_Trimmable): 

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

56 assumptions of this hypothesis. 

57 

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

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

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

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

62 """ 

63 

64 

65class ChoiceTooLarge(HypothesisException): 

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

67 

68 

69class CannotInvert(HypothesisException): 

70 """ 

71 Internal error raised by SearchStrategy._invert, either because the value 

72 is not produced by that strategy, or because we haven't implemented an 

73 inversion for it. 

74 """ 

75 

76 

77class Flaky(_Trimmable): 

78 """ 

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

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

81 

82 .. seealso:: 

83 

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

85 """ 

86 

87 

88class FlakyReplay(Flaky): 

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

90 detected during replay. 

91 

92 Carries information allowing the runner to reconstruct the flakiness as 

93 a FlakyFailure exception group for final presentation. 

94 """ 

95 

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

97 super().__init__(reason) 

98 self.reason = reason 

99 self._interesting_origins = interesting_origins 

100 

101 

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

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

104 return ", ".join( 

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

106 ) 

107 

108 

109class FlakyStrategyDefinition(Flaky): 

110 """ 

111 This function appears to cause inconsistent data generation. 

112 

113 Common causes for this problem are: 

114 1. The strategy 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 

118 .. seealso:: 

119 

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

121 """ 

122 

123 _BASE_MESSAGE = ( 

124 "Inconsistent data generation! Data generation behaved differently " 

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

126 "state?" 

127 ) 

128 

129 @classmethod 

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

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

132 

133 @classmethod 

134 def from_mismatch( 

135 cls, 

136 expected_type: str, 

137 expected_constraints: ChoiceConstraintsT, 

138 actual_type: str, 

139 actual_constraints: ChoiceConstraintsT, 

140 ) -> "FlakyStrategyDefinition": 

141 if actual_type != expected_type: 

142 detail = ( 

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

144 f" first: {expected_type}\n" 

145 f" second: {actual_type}\n" 

146 ) 

147 else: 

148 detail = ( 

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

150 "than the first.\n" 

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

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

153 ) 

154 return cls.with_detail(detail) 

155 

156 

157class _WrappedBaseException(Exception): 

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

159 

160 

161class FlakyFailure(ExceptionGroup, Flaky): 

162 """ 

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

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

165 did not fail, or caused a distinct error. 

166 

167 Common causes for this problem are: 

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

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

170 relevant state in from Hypothesis. 

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

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

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

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

175 don't do that and testing those instead. 

176 

177 .. seealso:: 

178 

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

180 """ 

181 

182 def __new__(cls, msg, group): 

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

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

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

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

187 group = list(group) 

188 for i, exc in enumerate(group): 

189 if not isinstance(exc, Exception): 

190 err = _WrappedBaseException() 

191 err.__cause__ = err.__context__ = exc 

192 group[i] = err 

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

194 

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

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

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

198 def derive(self, excs): 

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

200 

201 

202class FlakyBackendFailure(FlakyFailure): 

203 """ 

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

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

206 

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

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

209 does not reproduce, Hypothesis raises this exception. 

210 """ 

211 

212 

213class InvalidArgument(_Trimmable, TypeError): 

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

215 some manner incorrect.""" 

216 

217 

218class ResolutionFailed(InvalidArgument): 

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

220 

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

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

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

224 """ 

225 

226 

227class InvalidState(HypothesisException): 

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

229 

230 

231class InvalidDefinition(_Trimmable, TypeError): 

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

233 has something wrong with it.""" 

234 

235 

236class HypothesisWarning(HypothesisException, Warning): 

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

238 

239 

240class FailedHealthCheck(_Trimmable): 

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

242 

243 

244class NonInteractiveExampleWarning(HypothesisWarning): 

245 """ 

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

247 

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

249 part of a test suite. 

250 """ 

251 

252 

253class HypothesisDeprecationWarning(HypothesisWarning, FutureWarning): 

254 """A deprecation warning issued by Hypothesis. 

255 

256 Actually inherits from FutureWarning, because DeprecationWarning is 

257 hidden by the default warnings filter. 

258 

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

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

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

262 """ 

263 

264 

265class HypothesisSideeffectWarning(HypothesisWarning): 

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

267 discouraged at import or initialization time because they are 

268 slow or have user-visible side effects. 

269 """ 

270 

271 

272class Frozen(HypothesisException): 

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

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

275 

276 

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

278 if name == "MultipleFailures": 

279 from hypothesis.utils.deprecation import note_deprecation 

280 

281 note_deprecation( 

282 "MultipleFailures is deprecated; use the builtin `BaseExceptionGroup` type instead", 

283 since="2022-08-02", 

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

285 stacklevel=1, 

286 ) 

287 return BaseExceptionGroup 

288 

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

290 

291 

292class DeadlineExceeded(_Trimmable): 

293 """ 

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

295 setting. 

296 """ 

297 

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

299 super().__init__( 

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

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

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

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

304 ) 

305 self.runtime = runtime 

306 self.deadline = deadline 

307 

308 def __reduce__( 

309 self, 

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

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

312 

313 

314class StopTest(BaseException): 

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

316 the Hypothesis engine, which should then continue normally. 

317 """ 

318 

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

320 super().__init__(repr(testcounter)) 

321 self.testcounter = testcounter 

322 

323 

324class DidNotReproduce(HypothesisException): 

325 pass 

326 

327 

328class Found(HypothesisException): 

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

330 

331 

332class RewindRecursive(Exception): 

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

334 

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

336 self.target = target 

337 

338 

339class SmallSearchSpaceWarning(HypothesisWarning): 

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

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

342 

343 

344class NonRoundTrippableCharactersWarning(HypothesisWarning): 

345 """Issued when the ``codec`` argument to 

346 :func:`~hypothesis.strategies.characters` allows generating characters 

347 which encode successfully, but do not decode back to the same character. 

348 

349 Pass each such character in either ``include_characters`` or 

350 ``exclude_characters`` to silence this warning. 

351 """ 

352 

353 

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

355_valid_cannot_proceed_scopes = CannotProceedScopeT.__args__ # type: ignore 

356 

357 

358class BackendCannotProceed(HypothesisException): 

359 """ 

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

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

362 symbolic execution perhaps in |PrimitiveProvider.realize|. 

363 

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

365 

366 verified: 

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

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

369 Hypothesis will report unsound verification in the backend too. 

370 

371 exhausted: 

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

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

374 native code blocks symbolic reasoning very early. 

375 

376 discard_test_case: 

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

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

379 this backend. 

380 """ 

381 

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

383 if scope not in _valid_cannot_proceed_scopes: 

384 raise InvalidArgument( 

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

386 f"{_valid_cannot_proceed_scopes!r}" 

387 ) 

388 self.scope = scope