Perform approximate comparisons where the expected value is a datetime or timedelta. Requires an explicit tolerance as a timedelta for abs, or a float for rel. Relative tolerance is not supported for datetime comparisons.
| 564 | |
| 565 | |
| 566 | class ApproxTimedelta(ApproxBase): |
| 567 | """Perform approximate comparisons where the expected value is a |
| 568 | datetime or timedelta. |
| 569 | |
| 570 | Requires an explicit tolerance as a timedelta for abs, or a float for rel. |
| 571 | Relative tolerance is not supported for datetime comparisons. |
| 572 | """ |
| 573 | |
| 574 | def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None: |
| 575 | __tracebackhide__ = True |
| 576 | if isinstance(expected, datetime) and rel is not None: |
| 577 | raise TypeError( |
| 578 | "pytest.approx() does not support relative tolerance for " |
| 579 | "datetime comparisons. Use abs=timedelta(...) instead." |
| 580 | ) |
| 581 | if nan_ok: |
| 582 | raise TypeError( |
| 583 | "pytest.approx() does not support nan_ok for " |
| 584 | "datetime/timedelta comparisons." |
| 585 | ) |
| 586 | if abs is None and rel is None: |
| 587 | raise TypeError( |
| 588 | "pytest.approx() requires an explicit tolerance for " |
| 589 | "datetime/timedelta comparisons: " |
| 590 | "e.g. approx(expected, abs=timedelta(seconds=1)) " |
| 591 | "or approx(expected, rel=0.01)" |
| 592 | ) |
| 593 | if abs is not None and not isinstance(abs, timedelta): |
| 594 | raise TypeError( |
| 595 | f"absolute tolerance for datetime/timedelta must be a " |
| 596 | f"timedelta, got {type(abs).__name__}" |
| 597 | ) |
| 598 | if abs is not None and abs < timedelta(0): |
| 599 | raise ValueError(f"absolute tolerance can't be negative: {abs}") |
| 600 | if rel is not None: |
| 601 | if not isinstance(rel, (int, float)): |
| 602 | raise TypeError( |
| 603 | f"relative tolerance for timedelta must be a " |
| 604 | f"number, got {type(rel).__name__}" |
| 605 | ) |
| 606 | if rel < 0: |
| 607 | raise ValueError(f"relative tolerance can't be negative: {rel}") |
| 608 | if math.isnan(rel): |
| 609 | raise ValueError("relative tolerance can't be NaN.") |
| 610 | # Compute the effective tolerance. abs_tolerance is a timedelta, rel * expected |
| 611 | # gives a timedelta (timedelta * float works in Python). |
| 612 | abs_tolerance = abs |
| 613 | rel_tolerance = rel * builtins.abs(expected) if rel is not None else None |
| 614 | if abs_tolerance is not None and rel_tolerance is not None: |
| 615 | tolerance = max(abs_tolerance, rel_tolerance) |
| 616 | else: |
| 617 | tolerance = abs_tolerance if abs_tolerance is not None else rel_tolerance |
| 618 | super().__init__(expected, rel=rel, abs=tolerance, nan_ok=False) |
| 619 | |
| 620 | def __repr__(self) -> str: |
| 621 | return f"{self.expected} ± {self.abs}" |
| 622 | |
| 623 | def __eq__(self, actual) -> bool: |