Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/python_api.py: 45%

337 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-05-04 13:14 +0700

1import math 

2import pprint 

3from collections.abc import Collection 

4from collections.abc import Sized 

5from decimal import Decimal 

6from numbers import Complex 

7from types import TracebackType 

8from typing import Any 

9from typing import Callable 

10from typing import cast 

11from typing import ContextManager 

12from typing import List 

13from typing import Mapping 

14from typing import Optional 

15from typing import Pattern 

16from typing import Sequence 

17from typing import Tuple 

18from typing import Type 

19from typing import TYPE_CHECKING 

20from typing import TypeVar 

21from typing import Union 

22 

23if TYPE_CHECKING: 

24 from numpy import ndarray 

25 

26 

27import _pytest._code 

28from _pytest.compat import final 

29from _pytest.compat import STRING_TYPES 

30from _pytest.compat import overload 

31from _pytest.outcomes import fail 

32 

33 

34def _non_numeric_type_error(value, at: Optional[str]) -> TypeError: 

35 at_str = f" at {at}" if at else "" 

36 return TypeError( 

37 "cannot make approximate comparisons to non-numeric values: {!r} {}".format( 

38 value, at_str 

39 ) 

40 ) 

41 

42 

43def _compare_approx( 

44 full_object: object, 

45 message_data: Sequence[Tuple[str, str, str]], 

46 number_of_elements: int, 

47 different_ids: Sequence[object], 

48 max_abs_diff: float, 

49 max_rel_diff: float, 

50) -> List[str]: 

51 message_list = list(message_data) 

52 message_list.insert(0, ("Index", "Obtained", "Expected")) 

53 max_sizes = [0, 0, 0] 

54 for index, obtained, expected in message_list: 

55 max_sizes[0] = max(max_sizes[0], len(index)) 

56 max_sizes[1] = max(max_sizes[1], len(obtained)) 

57 max_sizes[2] = max(max_sizes[2], len(expected)) 

58 explanation = [ 

59 f"comparison failed. Mismatched elements: {len(different_ids)} / {number_of_elements}:", 

60 f"Max absolute difference: {max_abs_diff}", 

61 f"Max relative difference: {max_rel_diff}", 

62 ] + [ 

63 f"{indexes:<{max_sizes[0]}} | {obtained:<{max_sizes[1]}} | {expected:<{max_sizes[2]}}" 

64 for indexes, obtained, expected in message_list 

65 ] 

66 return explanation 

67 

68 

69# builtin pytest.approx helper 

70 

71 

72class ApproxBase: 

73 """Provide shared utilities for making approximate comparisons between 

74 numbers or sequences of numbers.""" 

75 

76 # Tell numpy to use our `__eq__` operator instead of its. 

77 __array_ufunc__ = None 

78 __array_priority__ = 100 

79 

80 def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None: 

81 __tracebackhide__ = True 

82 self.expected = expected 

83 self.abs = abs 

84 self.rel = rel 

85 self.nan_ok = nan_ok 

86 self._check_type() 

87 

88 def __repr__(self) -> str: 

89 raise NotImplementedError 

90 

91 def _repr_compare(self, other_side: Any) -> List[str]: 

92 return [ 

93 "comparison failed", 

94 f"Obtained: {other_side}", 

95 f"Expected: {self}", 

96 ] 

97 

98 def __eq__(self, actual) -> bool: 

99 return all( 

100 a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual) 

101 ) 

102 

103 def __bool__(self): 

104 __tracebackhide__ = True 

105 raise AssertionError( 

106 "approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?" 

107 ) 

108 

109 # Ignore type because of https://github.com/python/mypy/issues/4266. 

110 __hash__ = None # type: ignore 

111 

112 def __ne__(self, actual) -> bool: 

113 return not (actual == self) 

114 

115 def _approx_scalar(self, x) -> "ApproxScalar": 

116 if isinstance(x, Decimal): 

117 return ApproxDecimal(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) 

118 return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) 

119 

120 def _yield_comparisons(self, actual): 

121 """Yield all the pairs of numbers to be compared. 

122 

123 This is used to implement the `__eq__` method. 

124 """ 

125 raise NotImplementedError 

126 

127 def _check_type(self) -> None: 

128 """Raise a TypeError if the expected value is not a valid type.""" 

129 # This is only a concern if the expected value is a sequence. In every 

130 # other case, the approx() function ensures that the expected value has 

131 # a numeric type. For this reason, the default is to do nothing. The 

132 # classes that deal with sequences should reimplement this method to 

133 # raise if there are any non-numeric elements in the sequence. 

134 

135 

136def _recursive_sequence_map(f, x): 

137 """Recursively map a function over a sequence of arbitrary depth""" 

138 if isinstance(x, (list, tuple)): 

139 seq_type = type(x) 

140 return seq_type(_recursive_sequence_map(f, xi) for xi in x) 

141 else: 

142 return f(x) 

143 

144 

145class ApproxNumpy(ApproxBase): 

146 """Perform approximate comparisons where the expected value is numpy array.""" 

147 

148 def __repr__(self) -> str: 

149 list_scalars = _recursive_sequence_map( 

150 self._approx_scalar, self.expected.tolist() 

151 ) 

152 return f"approx({list_scalars!r})" 

153 

154 def _repr_compare(self, other_side: "ndarray") -> List[str]: 

155 import itertools 

156 import math 

157 

158 def get_value_from_nested_list( 

159 nested_list: List[Any], nd_index: Tuple[Any, ...] 

160 ) -> Any: 

161 """ 

162 Helper function to get the value out of a nested list, given an n-dimensional index. 

163 This mimics numpy's indexing, but for raw nested python lists. 

164 """ 

165 value: Any = nested_list 

166 for i in nd_index: 

167 value = value[i] 

168 return value 

169 

170 np_array_shape = self.expected.shape 

171 approx_side_as_seq = _recursive_sequence_map( 

172 self._approx_scalar, self.expected.tolist() 

173 ) 

174 

175 if np_array_shape != other_side.shape: 

176 return [ 

177 "Impossible to compare arrays with different shapes.", 

178 f"Shapes: {np_array_shape} and {other_side.shape}", 

179 ] 

180 

181 number_of_elements = self.expected.size 

182 max_abs_diff = -math.inf 

183 max_rel_diff = -math.inf 

184 different_ids = [] 

185 for index in itertools.product(*(range(i) for i in np_array_shape)): 

186 approx_value = get_value_from_nested_list(approx_side_as_seq, index) 

187 other_value = get_value_from_nested_list(other_side, index) 

188 if approx_value != other_value: 

189 abs_diff = abs(approx_value.expected - other_value) 

190 max_abs_diff = max(max_abs_diff, abs_diff) 

191 if other_value == 0.0: 

192 max_rel_diff = math.inf 

193 else: 

194 max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) 

195 different_ids.append(index) 

196 

197 message_data = [ 

198 ( 

199 str(index), 

200 str(get_value_from_nested_list(other_side, index)), 

201 str(get_value_from_nested_list(approx_side_as_seq, index)), 

202 ) 

203 for index in different_ids 

204 ] 

205 return _compare_approx( 

206 self.expected, 

207 message_data, 

208 number_of_elements, 

209 different_ids, 

210 max_abs_diff, 

211 max_rel_diff, 

212 ) 

213 

214 def __eq__(self, actual) -> bool: 

215 import numpy as np 

216 

217 # self.expected is supposed to always be an array here. 

218 

219 if not np.isscalar(actual): 

220 try: 

221 actual = np.asarray(actual) 

222 except Exception as e: 

223 raise TypeError(f"cannot compare '{actual}' to numpy.ndarray") from e 

224 

225 if not np.isscalar(actual) and actual.shape != self.expected.shape: 

226 return False 

227 

228 return super().__eq__(actual) 

229 

230 def _yield_comparisons(self, actual): 

231 import numpy as np 

232 

233 # `actual` can either be a numpy array or a scalar, it is treated in 

234 # `__eq__` before being passed to `ApproxBase.__eq__`, which is the 

235 # only method that calls this one. 

236 

237 if np.isscalar(actual): 

238 for i in np.ndindex(self.expected.shape): 

239 yield actual, self.expected[i].item() 

240 else: 

241 for i in np.ndindex(self.expected.shape): 

242 yield actual[i].item(), self.expected[i].item() 

243 

244 

245class ApproxMapping(ApproxBase): 

246 """Perform approximate comparisons where the expected value is a mapping 

247 with numeric values (the keys can be anything).""" 

248 

249 def __repr__(self) -> str: 

250 return "approx({!r})".format( 

251 {k: self._approx_scalar(v) for k, v in self.expected.items()} 

252 ) 

253 

254 def _repr_compare(self, other_side: Mapping[object, float]) -> List[str]: 

255 import math 

256 

257 approx_side_as_map = { 

258 k: self._approx_scalar(v) for k, v in self.expected.items() 

259 } 

260 

261 number_of_elements = len(approx_side_as_map) 

262 max_abs_diff = -math.inf 

263 max_rel_diff = -math.inf 

264 different_ids = [] 

265 for (approx_key, approx_value), other_value in zip( 

266 approx_side_as_map.items(), other_side.values() 

267 ): 

268 if approx_value != other_value: 

269 max_abs_diff = max( 

270 max_abs_diff, abs(approx_value.expected - other_value) 

271 ) 

272 if approx_value.expected == 0.0: 

273 max_rel_diff = math.inf 

274 else: 

275 max_rel_diff = max( 

276 max_rel_diff, 

277 abs( 

278 (approx_value.expected - other_value) 

279 / approx_value.expected 

280 ), 

281 ) 

282 different_ids.append(approx_key) 

283 

284 message_data = [ 

285 (str(key), str(other_side[key]), str(approx_side_as_map[key])) 

286 for key in different_ids 

287 ] 

288 

289 return _compare_approx( 

290 self.expected, 

291 message_data, 

292 number_of_elements, 

293 different_ids, 

294 max_abs_diff, 

295 max_rel_diff, 

296 ) 

297 

298 def __eq__(self, actual) -> bool: 

299 try: 

300 if set(actual.keys()) != set(self.expected.keys()): 

301 return False 

302 except AttributeError: 

303 return False 

304 

305 return super().__eq__(actual) 

306 

307 def _yield_comparisons(self, actual): 

308 for k in self.expected.keys(): 

309 yield actual[k], self.expected[k] 

310 

311 def _check_type(self) -> None: 

312 __tracebackhide__ = True 

313 for key, value in self.expected.items(): 

314 if isinstance(value, type(self.expected)): 

315 msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}" 

316 raise TypeError(msg.format(key, value, pprint.pformat(self.expected))) 

317 

318 

319class ApproxSequenceLike(ApproxBase): 

320 """Perform approximate comparisons where the expected value is a sequence of numbers.""" 

321 

322 def __repr__(self) -> str: 

323 seq_type = type(self.expected) 

324 if seq_type not in (tuple, list): 

325 seq_type = list 

326 return "approx({!r})".format( 

327 seq_type(self._approx_scalar(x) for x in self.expected) 

328 ) 

329 

330 def _repr_compare(self, other_side: Sequence[float]) -> List[str]: 

331 import math 

332 

333 if len(self.expected) != len(other_side): 

334 return [ 

335 "Impossible to compare lists with different sizes.", 

336 f"Lengths: {len(self.expected)} and {len(other_side)}", 

337 ] 

338 

339 approx_side_as_map = _recursive_sequence_map(self._approx_scalar, self.expected) 

340 

341 number_of_elements = len(approx_side_as_map) 

342 max_abs_diff = -math.inf 

343 max_rel_diff = -math.inf 

344 different_ids = [] 

345 for i, (approx_value, other_value) in enumerate( 

346 zip(approx_side_as_map, other_side) 

347 ): 

348 if approx_value != other_value: 

349 abs_diff = abs(approx_value.expected - other_value) 

350 max_abs_diff = max(max_abs_diff, abs_diff) 

351 if other_value == 0.0: 

352 max_rel_diff = math.inf 

353 else: 

354 max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) 

355 different_ids.append(i) 

356 

357 message_data = [ 

358 (str(i), str(other_side[i]), str(approx_side_as_map[i])) 

359 for i in different_ids 

360 ] 

361 

362 return _compare_approx( 

363 self.expected, 

364 message_data, 

365 number_of_elements, 

366 different_ids, 

367 max_abs_diff, 

368 max_rel_diff, 

369 ) 

370 

371 def __eq__(self, actual) -> bool: 

372 try: 

373 if len(actual) != len(self.expected): 

374 return False 

375 except TypeError: 

376 return False 

377 return super().__eq__(actual) 

378 

379 def _yield_comparisons(self, actual): 

380 return zip(actual, self.expected) 

381 

382 def _check_type(self) -> None: 

383 __tracebackhide__ = True 

384 for index, x in enumerate(self.expected): 

385 if isinstance(x, type(self.expected)): 

386 msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}" 

387 raise TypeError(msg.format(x, index, pprint.pformat(self.expected))) 

388 

389 

390class ApproxScalar(ApproxBase): 

391 """Perform approximate comparisons where the expected value is a single number.""" 

392 

393 # Using Real should be better than this Union, but not possible yet: 

394 # https://github.com/python/typeshed/pull/3108 

395 DEFAULT_ABSOLUTE_TOLERANCE: Union[float, Decimal] = 1e-12 

396 DEFAULT_RELATIVE_TOLERANCE: Union[float, Decimal] = 1e-6 

397 

398 def __repr__(self) -> str: 

399 """Return a string communicating both the expected value and the 

400 tolerance for the comparison being made. 

401 

402 For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``. 

403 """ 

404 # Don't show a tolerance for values that aren't compared using 

405 # tolerances, i.e. non-numerics and infinities. Need to call abs to 

406 # handle complex numbers, e.g. (inf + 1j). 

407 if (not isinstance(self.expected, (Complex, Decimal))) or math.isinf( 

408 abs(self.expected) # type: ignore[arg-type] 

409 ): 

410 return str(self.expected) 

411 

412 # If a sensible tolerance can't be calculated, self.tolerance will 

413 # raise a ValueError. In this case, display '???'. 

414 try: 

415 vetted_tolerance = f"{self.tolerance:.1e}" 

416 if ( 

417 isinstance(self.expected, Complex) 

418 and self.expected.imag 

419 and not math.isinf(self.tolerance) 

420 ): 

421 vetted_tolerance += " ∠ ±180°" 

422 except ValueError: 

423 vetted_tolerance = "???" 

424 

425 return f"{self.expected} ± {vetted_tolerance}" 

426 

427 def __eq__(self, actual) -> bool: 

428 """Return whether the given value is equal to the expected value 

429 within the pre-specified tolerance.""" 

430 asarray = _as_numpy_array(actual) 

431 if asarray is not None: 

432 # Call ``__eq__()`` manually to prevent infinite-recursion with 

433 # numpy<1.13. See #3748. 

434 return all(self.__eq__(a) for a in asarray.flat) 

435 

436 # Short-circuit exact equality. 

437 if actual == self.expected: 

438 return True 

439 

440 # If either type is non-numeric, fall back to strict equality. 

441 # NB: we need Complex, rather than just Number, to ensure that __abs__, 

442 # __sub__, and __float__ are defined. 

443 if not ( 

444 isinstance(self.expected, (Complex, Decimal)) 

445 and isinstance(actual, (Complex, Decimal)) 

446 ): 

447 return False 

448 

449 # Allow the user to control whether NaNs are considered equal to each 

450 # other or not. The abs() calls are for compatibility with complex 

451 # numbers. 

452 if math.isnan(abs(self.expected)): # type: ignore[arg-type] 

453 return self.nan_ok and math.isnan(abs(actual)) # type: ignore[arg-type] 

454 

455 # Infinity shouldn't be approximately equal to anything but itself, but 

456 # if there's a relative tolerance, it will be infinite and infinity 

457 # will seem approximately equal to everything. The equal-to-itself 

458 # case would have been short circuited above, so here we can just 

459 # return false if the expected value is infinite. The abs() call is 

460 # for compatibility with complex numbers. 

461 if math.isinf(abs(self.expected)): # type: ignore[arg-type] 

462 return False 

463 

464 # Return true if the two numbers are within the tolerance. 

465 result: bool = abs(self.expected - actual) <= self.tolerance 

466 return result 

467 

468 # Ignore type because of https://github.com/python/mypy/issues/4266. 

469 __hash__ = None # type: ignore 

470 

471 @property 

472 def tolerance(self): 

473 """Return the tolerance for the comparison. 

474 

475 This could be either an absolute tolerance or a relative tolerance, 

476 depending on what the user specified or which would be larger. 

477 """ 

478 

479 def set_default(x, default): 

480 return x if x is not None else default 

481 

482 # Figure out what the absolute tolerance should be. ``self.abs`` is 

483 # either None or a value specified by the user. 

484 absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE) 

485 

486 if absolute_tolerance < 0: 

487 raise ValueError( 

488 f"absolute tolerance can't be negative: {absolute_tolerance}" 

489 ) 

490 if math.isnan(absolute_tolerance): 

491 raise ValueError("absolute tolerance can't be NaN.") 

492 

493 # If the user specified an absolute tolerance but not a relative one, 

494 # just return the absolute tolerance. 

495 if self.rel is None: 

496 if self.abs is not None: 

497 return absolute_tolerance 

498 

499 # Figure out what the relative tolerance should be. ``self.rel`` is 

500 # either None or a value specified by the user. This is done after 

501 # we've made sure the user didn't ask for an absolute tolerance only, 

502 # because we don't want to raise errors about the relative tolerance if 

503 # we aren't even going to use it. 

504 relative_tolerance = set_default( 

505 self.rel, self.DEFAULT_RELATIVE_TOLERANCE 

506 ) * abs(self.expected) 

507 

508 if relative_tolerance < 0: 

509 raise ValueError( 

510 f"relative tolerance can't be negative: {relative_tolerance}" 

511 ) 

512 if math.isnan(relative_tolerance): 

513 raise ValueError("relative tolerance can't be NaN.") 

514 

515 # Return the larger of the relative and absolute tolerances. 

516 return max(relative_tolerance, absolute_tolerance) 

517 

518 

519class ApproxDecimal(ApproxScalar): 

520 """Perform approximate comparisons where the expected value is a Decimal.""" 

521 

522 DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12") 

523 DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6") 

524 

525 

526def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: 

527 """Assert that two numbers (or two ordered sequences of numbers) are equal to each other 

528 within some tolerance. 

529 

530 Due to the :doc:`python:tutorial/floatingpoint`, numbers that we 

531 would intuitively expect to be equal are not always so:: 

532 

533 >>> 0.1 + 0.2 == 0.3 

534 False 

535 

536 This problem is commonly encountered when writing tests, e.g. when making 

537 sure that floating-point values are what you expect them to be. One way to 

538 deal with this problem is to assert that two floating-point numbers are 

539 equal to within some appropriate tolerance:: 

540 

541 >>> abs((0.1 + 0.2) - 0.3) < 1e-6 

542 True 

543 

544 However, comparisons like this are tedious to write and difficult to 

545 understand. Furthermore, absolute comparisons like the one above are 

546 usually discouraged because there's no tolerance that works well for all 

547 situations. ``1e-6`` is good for numbers around ``1``, but too small for 

548 very big numbers and too big for very small ones. It's better to express 

549 the tolerance as a fraction of the expected value, but relative comparisons 

550 like that are even more difficult to write correctly and concisely. 

551 

552 The ``approx`` class performs floating-point comparisons using a syntax 

553 that's as intuitive as possible:: 

554 

555 >>> from pytest import approx 

556 >>> 0.1 + 0.2 == approx(0.3) 

557 True 

558 

559 The same syntax also works for ordered sequences of numbers:: 

560 

561 >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) 

562 True 

563 

564 ``numpy`` arrays:: 

565 

566 >>> import numpy as np # doctest: +SKIP 

567 >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP 

568 True 

569 

570 And for a ``numpy`` array against a scalar:: 

571 

572 >>> import numpy as np # doctest: +SKIP 

573 >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP 

574 True 

575 

576 Only ordered sequences are supported, because ``approx`` needs 

577 to infer the relative position of the sequences without ambiguity. This means 

578 ``sets`` and other unordered sequences are not supported. 

579 

580 Finally, dictionary *values* can also be compared:: 

581 

582 >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6}) 

583 True 

584 

585 The comparison will be true if both mappings have the same keys and their 

586 respective values match the expected tolerances. 

587 

588 **Tolerances** 

589 

590 By default, ``approx`` considers numbers within a relative tolerance of 

591 ``1e-6`` (i.e. one part in a million) of its expected value to be equal. 

592 This treatment would lead to surprising results if the expected value was 

593 ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``. 

594 To handle this case less surprisingly, ``approx`` also considers numbers 

595 within an absolute tolerance of ``1e-12`` of its expected value to be 

596 equal. Infinity and NaN are special cases. Infinity is only considered 

597 equal to itself, regardless of the relative tolerance. NaN is not 

598 considered equal to anything by default, but you can make it be equal to 

599 itself by setting the ``nan_ok`` argument to True. (This is meant to 

600 facilitate comparing arrays that use NaN to mean "no data".) 

601 

602 Both the relative and absolute tolerances can be changed by passing 

603 arguments to the ``approx`` constructor:: 

604 

605 >>> 1.0001 == approx(1) 

606 False 

607 >>> 1.0001 == approx(1, rel=1e-3) 

608 True 

609 >>> 1.0001 == approx(1, abs=1e-3) 

610 True 

611 

612 If you specify ``abs`` but not ``rel``, the comparison will not consider 

613 the relative tolerance at all. In other words, two numbers that are within 

614 the default relative tolerance of ``1e-6`` will still be considered unequal 

615 if they exceed the specified absolute tolerance. If you specify both 

616 ``abs`` and ``rel``, the numbers will be considered equal if either 

617 tolerance is met:: 

618 

619 >>> 1 + 1e-8 == approx(1) 

620 True 

621 >>> 1 + 1e-8 == approx(1, abs=1e-12) 

622 False 

623 >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12) 

624 True 

625 

626 You can also use ``approx`` to compare nonnumeric types, or dicts and 

627 sequences containing nonnumeric types, in which case it falls back to 

628 strict equality. This can be useful for comparing dicts and sequences that 

629 can contain optional values:: 

630 

631 >>> {"required": 1.0000005, "optional": None} == approx({"required": 1, "optional": None}) 

632 True 

633 >>> [None, 1.0000005] == approx([None,1]) 

634 True 

635 >>> ["foo", 1.0000005] == approx([None,1]) 

636 False 

637 

638 If you're thinking about using ``approx``, then you might want to know how 

639 it compares to other good ways of comparing floating-point numbers. All of 

640 these algorithms are based on relative and absolute tolerances and should 

641 agree for the most part, but they do have meaningful differences: 

642 

643 - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative 

644 tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute 

645 tolerance is met. Because the relative tolerance is calculated w.r.t. 

646 both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor 

647 ``b`` is a "reference value"). You have to specify an absolute tolerance 

648 if you want to compare to ``0.0`` because there is no tolerance by 

649 default. More information: :py:func:`math.isclose`. 

650 

651 - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference 

652 between ``a`` and ``b`` is less that the sum of the relative tolerance 

653 w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance 

654 is only calculated w.r.t. ``b``, this test is asymmetric and you can 

655 think of ``b`` as the reference value. Support for comparing sequences 

656 is provided by :py:func:`numpy.allclose`. More information: 

657 :std:doc:`numpy:reference/generated/numpy.isclose`. 

658 

659 - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` 

660 are within an absolute tolerance of ``1e-7``. No relative tolerance is 

661 considered , so this function is not appropriate for very large or very 

662 small numbers. Also, it's only available in subclasses of ``unittest.TestCase`` 

663 and it's ugly because it doesn't follow PEP8. More information: 

664 :py:meth:`unittest.TestCase.assertAlmostEqual`. 

665 

666 - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative 

667 tolerance is met w.r.t. ``b`` or if the absolute tolerance is met. 

668 Because the relative tolerance is only calculated w.r.t. ``b``, this test 

669 is asymmetric and you can think of ``b`` as the reference value. In the 

670 special case that you explicitly specify an absolute tolerance but not a 

671 relative tolerance, only the absolute tolerance is considered. 

672 

673 .. note:: 

674 

675 ``approx`` can handle numpy arrays, but we recommend the 

676 specialised test helpers in :std:doc:`numpy:reference/routines.testing` 

677 if you need support for comparisons, NaNs, or ULP-based tolerances. 

678 

679 To match strings using regex, you can use 

680 `Matches <https://github.com/asottile/re-assert#re_assertmatchespattern-str-args-kwargs>`_ 

681 from the 

682 `re_assert package <https://github.com/asottile/re-assert>`_. 

683 

684 .. warning:: 

685 

686 .. versionchanged:: 3.2 

687 

688 In order to avoid inconsistent behavior, :py:exc:`TypeError` is 

689 raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons. 

690 The example below illustrates the problem:: 

691 

692 assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10) 

693 assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10) 

694 

695 In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)`` 

696 to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to 

697 comparison. This is because the call hierarchy of rich comparisons 

698 follows a fixed behavior. More information: :py:meth:`object.__ge__` 

699 

700 .. versionchanged:: 3.7.1 

701 ``approx`` raises ``TypeError`` when it encounters a dict value or 

702 sequence element of nonnumeric type. 

703 

704 .. versionchanged:: 6.1.0 

705 ``approx`` falls back to strict equality for nonnumeric types instead 

706 of raising ``TypeError``. 

707 """ 

708 

709 # Delegate the comparison to a class that knows how to deal with the type 

710 # of the expected value (e.g. int, float, list, dict, numpy.array, etc). 

711 # 

712 # The primary responsibility of these classes is to implement ``__eq__()`` 

713 # and ``__repr__()``. The former is used to actually check if some 

714 # "actual" value is equivalent to the given expected value within the 

715 # allowed tolerance. The latter is used to show the user the expected 

716 # value and tolerance, in the case that a test failed. 

717 # 

718 # The actual logic for making approximate comparisons can be found in 

719 # ApproxScalar, which is used to compare individual numbers. All of the 

720 # other Approx classes eventually delegate to this class. The ApproxBase 

721 # class provides some convenient methods and overloads, but isn't really 

722 # essential. 

723 

724 __tracebackhide__ = True 

725 

726 if isinstance(expected, Decimal): 

727 cls: Type[ApproxBase] = ApproxDecimal 

728 elif isinstance(expected, Mapping): 

729 cls = ApproxMapping 

730 elif _is_numpy_array(expected): 

731 expected = _as_numpy_array(expected) 

732 cls = ApproxNumpy 

733 elif ( 

734 hasattr(expected, "__getitem__") 

735 and isinstance(expected, Sized) 

736 # Type ignored because the error is wrong -- not unreachable. 

737 and not isinstance(expected, STRING_TYPES) # type: ignore[unreachable] 

738 ): 

739 cls = ApproxSequenceLike 

740 elif ( 

741 isinstance(expected, Collection) 

742 # Type ignored because the error is wrong -- not unreachable. 

743 and not isinstance(expected, STRING_TYPES) # type: ignore[unreachable] 

744 ): 

745 msg = f"pytest.approx() only supports ordered sequences, but got: {repr(expected)}" 

746 raise TypeError(msg) 

747 else: 

748 cls = ApproxScalar 

749 

750 return cls(expected, rel, abs, nan_ok) 

751 

752 

753def _is_numpy_array(obj: object) -> bool: 

754 """ 

755 Return true if the given object is implicitly convertible to ndarray, 

756 and numpy is already imported. 

757 """ 

758 return _as_numpy_array(obj) is not None 

759 

760 

761def _as_numpy_array(obj: object) -> Optional["ndarray"]: 

762 """ 

763 Return an ndarray if the given object is implicitly convertible to ndarray, 

764 and numpy is already imported, otherwise None. 

765 """ 

766 import sys 

767 

768 np: Any = sys.modules.get("numpy") 

769 if np is not None: 

770 # avoid infinite recursion on numpy scalars, which have __array__ 

771 if np.isscalar(obj): 

772 return None 

773 elif isinstance(obj, np.ndarray): 

774 return obj 

775 elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): 

776 return np.asarray(obj) 

777 return None 

778 

779 

780# builtin pytest.raises helper 

781 

782E = TypeVar("E", bound=BaseException) 

783 

784 

785@overload 

786def raises( 

787 expected_exception: Union[Type[E], Tuple[Type[E], ...]], 

788 *, 

789 match: Optional[Union[str, Pattern[str]]] = ..., 

790) -> "RaisesContext[E]": 

791 ... 

792 

793 

794@overload 

795def raises( # noqa: F811 

796 expected_exception: Union[Type[E], Tuple[Type[E], ...]], 

797 func: Callable[..., Any], 

798 *args: Any, 

799 **kwargs: Any, 

800) -> _pytest._code.ExceptionInfo[E]: 

801 ... 

802 

803 

804def raises( # noqa: F811 

805 expected_exception: Union[Type[E], Tuple[Type[E], ...]], *args: Any, **kwargs: Any 

806) -> Union["RaisesContext[E]", _pytest._code.ExceptionInfo[E]]: 

807 r"""Assert that a code block/function call raises an exception. 

808 

809 :param typing.Type[E] | typing.Tuple[typing.Type[E], ...] expected_exception: 

810 The excpected exception type, or a tuple if one of multiple possible 

811 exception types are excepted. 

812 :kwparam str | typing.Pattern[str] | None match: 

813 If specified, a string containing a regular expression, 

814 or a regular expression object, that is tested against the string 

815 representation of the exception using :func:`re.search`. 

816 

817 To match a literal string that may contain :ref:`special characters 

818 <re-syntax>`, the pattern can first be escaped with :func:`re.escape`. 

819 

820 (This is only used when :py:func:`pytest.raises` is used as a context manager, 

821 and passed through to the function otherwise. 

822 When using :py:func:`pytest.raises` as a function, you can use: 

823 ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.) 

824 

825 .. currentmodule:: _pytest._code 

826 

827 Use ``pytest.raises`` as a context manager, which will capture the exception of the given 

828 type:: 

829 

830 >>> import pytest 

831 >>> with pytest.raises(ZeroDivisionError): 

832 ... 1/0 

833 

834 If the code block does not raise the expected exception (``ZeroDivisionError`` in the example 

835 above), or no exception at all, the check will fail instead. 

836 

837 You can also use the keyword argument ``match`` to assert that the 

838 exception matches a text or regex:: 

839 

840 >>> with pytest.raises(ValueError, match='must be 0 or None'): 

841 ... raise ValueError("value must be 0 or None") 

842 

843 >>> with pytest.raises(ValueError, match=r'must be \d+$'): 

844 ... raise ValueError("value must be 42") 

845 

846 The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the 

847 details of the captured exception:: 

848 

849 >>> with pytest.raises(ValueError) as exc_info: 

850 ... raise ValueError("value must be 42") 

851 >>> assert exc_info.type is ValueError 

852 >>> assert exc_info.value.args[0] == "value must be 42" 

853 

854 .. note:: 

855 

856 When using ``pytest.raises`` as a context manager, it's worthwhile to 

857 note that normal context manager rules apply and that the exception 

858 raised *must* be the final line in the scope of the context manager. 

859 Lines of code after that, within the scope of the context manager will 

860 not be executed. For example:: 

861 

862 >>> value = 15 

863 >>> with pytest.raises(ValueError) as exc_info: 

864 ... if value > 10: 

865 ... raise ValueError("value must be <= 10") 

866 ... assert exc_info.type is ValueError # this will not execute 

867 

868 Instead, the following approach must be taken (note the difference in 

869 scope):: 

870 

871 >>> with pytest.raises(ValueError) as exc_info: 

872 ... if value > 10: 

873 ... raise ValueError("value must be <= 10") 

874 ... 

875 >>> assert exc_info.type is ValueError 

876 

877 **Using with** ``pytest.mark.parametrize`` 

878 

879 When using :ref:`pytest.mark.parametrize ref` 

880 it is possible to parametrize tests such that 

881 some runs raise an exception and others do not. 

882 

883 See :ref:`parametrizing_conditional_raising` for an example. 

884 

885 **Legacy form** 

886 

887 It is possible to specify a callable by passing a to-be-called lambda:: 

888 

889 >>> raises(ZeroDivisionError, lambda: 1/0) 

890 <ExceptionInfo ...> 

891 

892 or you can specify an arbitrary callable with arguments:: 

893 

894 >>> def f(x): return 1/x 

895 ... 

896 >>> raises(ZeroDivisionError, f, 0) 

897 <ExceptionInfo ...> 

898 >>> raises(ZeroDivisionError, f, x=0) 

899 <ExceptionInfo ...> 

900 

901 The form above is fully supported but discouraged for new code because the 

902 context manager form is regarded as more readable and less error-prone. 

903 

904 .. note:: 

905 Similar to caught exception objects in Python, explicitly clearing 

906 local references to returned ``ExceptionInfo`` objects can 

907 help the Python interpreter speed up its garbage collection. 

908 

909 Clearing those references breaks a reference cycle 

910 (``ExceptionInfo`` --> caught exception --> frame stack raising 

911 the exception --> current frame stack --> local variables --> 

912 ``ExceptionInfo``) which makes Python keep all objects referenced 

913 from that cycle (including all local variables in the current 

914 frame) alive until the next cyclic garbage collection run. 

915 More detailed information can be found in the official Python 

916 documentation for :ref:`the try statement <python:try>`. 

917 """ 

918 __tracebackhide__ = True 

919 

920 if not expected_exception: 

921 raise ValueError( 

922 f"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. " 

923 f"Raising exceptions is already understood as failing the test, so you don't need " 

924 f"any special code to say 'this should never raise an exception'." 

925 ) 

926 if isinstance(expected_exception, type): 

927 excepted_exceptions: Tuple[Type[E], ...] = (expected_exception,) 

928 else: 

929 excepted_exceptions = expected_exception 

930 for exc in excepted_exceptions: 

931 if not isinstance(exc, type) or not issubclass(exc, BaseException): 

932 msg = "expected exception must be a BaseException type, not {}" # type: ignore[unreachable] 

933 not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__ 

934 raise TypeError(msg.format(not_a)) 

935 

936 message = f"DID NOT RAISE {expected_exception}" 

937 

938 if not args: 

939 match: Optional[Union[str, Pattern[str]]] = kwargs.pop("match", None) 

940 if kwargs: 

941 msg = "Unexpected keyword arguments passed to pytest.raises: " 

942 msg += ", ".join(sorted(kwargs)) 

943 msg += "\nUse context-manager form instead?" 

944 raise TypeError(msg) 

945 return RaisesContext(expected_exception, message, match) 

946 else: 

947 func = args[0] 

948 if not callable(func): 

949 raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") 

950 try: 

951 func(*args[1:], **kwargs) 

952 except expected_exception as e: 

953 # We just caught the exception - there is a traceback. 

954 assert e.__traceback__ is not None 

955 return _pytest._code.ExceptionInfo.from_exc_info( 

956 (type(e), e, e.__traceback__) 

957 ) 

958 fail(message) 

959 

960 

961# This doesn't work with mypy for now. Use fail.Exception instead. 

962raises.Exception = fail.Exception # type: ignore 

963 

964 

965@final 

966class RaisesContext(ContextManager[_pytest._code.ExceptionInfo[E]]): 

967 def __init__( 

968 self, 

969 expected_exception: Union[Type[E], Tuple[Type[E], ...]], 

970 message: str, 

971 match_expr: Optional[Union[str, Pattern[str]]] = None, 

972 ) -> None: 

973 self.expected_exception = expected_exception 

974 self.message = message 

975 self.match_expr = match_expr 

976 self.excinfo: Optional[_pytest._code.ExceptionInfo[E]] = None 

977 

978 def __enter__(self) -> _pytest._code.ExceptionInfo[E]: 

979 self.excinfo = _pytest._code.ExceptionInfo.for_later() 

980 return self.excinfo 

981 

982 def __exit__( 

983 self, 

984 exc_type: Optional[Type[BaseException]], 

985 exc_val: Optional[BaseException], 

986 exc_tb: Optional[TracebackType], 

987 ) -> bool: 

988 __tracebackhide__ = True 

989 if exc_type is None: 

990 fail(self.message) 

991 assert self.excinfo is not None 

992 if not issubclass(exc_type, self.expected_exception): 

993 return False 

994 # Cast to narrow the exception type now that it's verified. 

995 exc_info = cast(Tuple[Type[E], E, TracebackType], (exc_type, exc_val, exc_tb)) 

996 self.excinfo.fill_unfilled(exc_info) 

997 if self.match_expr is not None: 

998 self.excinfo.match(self.match_expr) 

999 return True