pytest-dev/pytest · error · TypeError

expected value must support abs(...) when relative…

Error message

expected value must support abs(...) when relative tolerance is used, got {type(expected).__name__}

What it means

When a relative tolerance is supplied to ApproxScalar, the expected value must support abs() (typing.SupportsAbs) because relative tolerance is computed as rel * abs(expected). pytest rejects non-numeric expected values (strings, None, custom objects without __abs__) up front rather than failing later inside the tolerance property. Without a rel argument this check is skipped and approx falls back to strict equality for non-numeric types.

Solutions

  1. Drop the rel argument when the expected value is non-numeric: `approx(expected)` uses strict equality.
  2. Convert the expected value to a numeric type before passing: `approx(float(expected), rel=0.01)`.
  3. Branch in helper code: only pass rel when isinstance(expected, (int, float, Decimal)).

Example fix

# before
approx('3.14', rel=0.01)
# after
approx(float('3.14'), rel=0.01)
Defensive patterns

Strategy: type-guard

Validate before calling

from typing import SupportsAbs
def expected_supports_abs(expected) -> bool:
    return isinstance(expected, SupportsAbs)
# usage
if rel is not None and not expected_supports_abs(expected):
    raise TypeError('expected must support abs() when rel is set; dropping rel for non-numeric')
assert actual == approx(expected, rel=rel if expected_supports_abs(expected) else None)

Type guard

from typing import SupportsAbs
def supports_abs(v) -> bool:
    return isinstance(v, SupportsAbs) and hasattr(v, '__abs__')

Prevention

When it happens

Trigger: Calling approx('somestring', rel=0.1), approx(None, rel=0.1), approx(SomeObj(), rel=0.1). Triggered at ApproxScalar.__init__ (src/_pytest/approx.py:454-458). Note: bool passes SupportsAbs but is treated as non-numeric elsewhere.

Common situations: Using a tolerance on a value loaded from JSON as a string; comparing identifiers or labels but accidentally passing rel; generic helpers that always pass rel regardless of value type.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/af23743444ccb4f2. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/approx.py:455

    rel: float | Decimal | None
    abs: float | Decimal | None

    def __init__(
        self,
        expected: ExpectedT,
        rel: float | Decimal | timedelta | None,
        abs: float | Decimal | timedelta | None,
        nan_ok: bool,
    ) -> None:
        __tracebackhide__ = True
        if rel is not None:
            if not isinstance(rel, (int, float, Decimal)):
                raise TypeError(
                    f"relative tolerance for a scalar value must be an int, float or Decimal, "
                    f"got {type(rel).__name__}"
                )
            if not isinstance(expected, SupportsAbs):
                raise TypeError(
                    f"expected value must support abs(...) when relative tolerance is used, "
                    f"got {type(expected).__name__}"
                )
        if abs is not None and not isinstance(abs, (int, float, Decimal)):
            raise TypeError(
                f"absolute tolerance for a scalar value must be an int, float or Decimal, "
                f"got {type(abs).__name__}"
            )
        super().__init__(expected, rel=rel, abs=abs, nan_ok=nan_ok)

    def __repr__(self) -> str:
        """Return a string communicating both the expected value and the
        tolerance for the comparison being made.

        For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``.
        """
        # Don't show a tolerance for values that aren't compared using
        # tolerances, i.e. non-numerics and infinities. Need to call abs to

View on GitHub (pinned to 0d6fbdeffa)