pytest-dev/pytest · error · TypeError

pytest.approx() only supports ordered sequences, but got

Error message

pytest.approx() only supports ordered sequences, but got: {expected!r}

What it means

Raised by the approx() factory when expected is a Collection (and not str/bytes) but is not ordered — i.e., it lacks __getitem__ or is not a Sequence. The dispatch first checks Decimal, Mapping, numpy array, then sequence-like objects; an unordered collection (e.g. a set) falls through to the Collection branch and is rejected because approx comparison needs positional correspondence. datetime/timedelta and scalars are handled in later branches.

Solutions

  1. Convert to a sorted list: approx(sorted(my_set)).
  2. Convert to a tuple preserving intended order: approx(tuple(my_collection)).
  3. For unordered comparison, compare as a set explicitly rather than via approx().

Example fix

// before
assert result_set == approx({1, 2, 3})
// after
assert sorted(result_set) == approx(sorted([1, 2, 3]))
Defensive patterns

Strategy: type-guard

Validate before calling

from collections.abc import Collection, Sequence
def approx_collection(expected, **kw):
    if isinstance(expected, Collection) and not isinstance(expected, (str, bytes, Sequence)):
        expected = sorted(expected)
    return approx(expected, **kw)

Type guard

from collections.abc import Sequence
def is_approximable_sequence(v) -> bool:
    return isinstance(v, Sequence) or hasattr(v, '__getitem__')

Prevention

When it happens

Trigger: approx({1, 2, 3}) (a set); approx(frozenset(...)); approx(some_custom_collection_without_getitem).

Common situations: Asserting on set-valued results without converting to a sorted list; using approx on unordered collection types; custom collections that register as Collection but aren't subscriptable.

Related errors


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

Appendix: source

Thrown at src/_pytest/approx.py:967

    # The actual logic for making approximate comparisons can be found in
    # ApproxScalar, which is used to compare individual numbers.  All of the
    # other Approx classes eventually delegate to this class.  The ApproxBase
    # class provides some convenient methods and overloads, but isn't really
    # essential.

    __tracebackhide__ = True

    if isinstance(expected, Decimal):
        return ApproxDecimal(expected, rel=rel, abs=abs, nan_ok=nan_ok)  # type: ignore[return-value]
    elif isinstance(expected, Mapping):
        return ApproxMapping(expected, rel=rel, abs=abs, nan_ok=nan_ok)  # type: ignore[return-value]
    elif (np_array := _as_numpy_array(expected)) is not None:
        return ApproxNumpy(np_array, rel=rel, abs=abs, nan_ok=nan_ok)
    elif _is_sequence_like(expected):
        return ApproxSequenceLike(expected, rel=rel, abs=abs, nan_ok=nan_ok)  # type: ignore[return-value]
    elif isinstance(expected, Collection) and not isinstance(expected, str | bytes):
        msg = f"pytest.approx() only supports ordered sequences, but got: {expected!r}"
        raise TypeError(msg)
    elif isinstance(expected, (datetime, timedelta)):
        return ApproxTimedelta(expected, rel=rel, abs=abs, nan_ok=nan_ok)  # type: ignore[return-value]
    else:
        return ApproxScalar(expected, rel=rel, abs=abs, nan_ok=nan_ok)


def _is_sequence_like(expected: object) -> TypeGuard[Sequence[Any]]:
    return (
        hasattr(expected, "__getitem__")
        and isinstance(expected, Sized)
        and not isinstance(expected, str | bytes)
    )


def _as_numpy_array(obj: object) -> ndarray | None:
    """
    Return an ndarray if the given object is implicitly convertible to ndarray,
    and numpy is already imported, otherwise None.

View on GitHub (pinned to 0d6fbdeffa)