Skip to content

Containment assertions

The contains family, shared by strings, collections, dicts, and bytes.

Containment assertions mixin.

contains

contains(*items: object) -> Self

Asserts that val contains the given item or items.

Checks if the collection contains the given item or items using in operator.

Parameters:

Name Type Description Default
*items object

the item or items expected to be contained

()

Examples:

Usage:

assert_that('foo').contains('f')
assert_that('foo').contains('f', 'oo')
assert_that(['a', 'b']).contains('b', 'a')
assert_that((1, 2, 3)).contains(3, 2, 1)
assert_that({'a': 1, 'b': 2}).contains('b', 'a')  # checks keys
assert_that({'a', 'b'}).contains('b', 'a')
assert_that([1, 2, 3]).is_type_of(list).contains(1, 2).does_not_contain(4, 5)

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val does not contain the item or items

Tip

Use the contains_key() alias when working with dict-like objects to be self-documenting.

See Also

contains_ignoring_case() - for case-insensitive string contains

Source code in assertpy2/contains.py
def contains(self, *items: object) -> Self:
    """Asserts that val contains the given item or items.

    Checks if the collection contains the given item or items using ``in`` operator.

    Args:
        *items: the item or items expected to be contained

    Examples:
        Usage:

            assert_that('foo').contains('f')
            assert_that('foo').contains('f', 'oo')
            assert_that(['a', 'b']).contains('b', 'a')
            assert_that((1, 2, 3)).contains(3, 2, 1)
            assert_that({'a': 1, 'b': 2}).contains('b', 'a')  # checks keys
            assert_that({'a', 'b'}).contains('b', 'a')
            assert_that([1, 2, 3]).is_type_of(list).contains(1, 2).does_not_contain(4, 5)

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val does **not** contain the item or items

    Tip:
        Use the [`contains_key()`][assertpy2.dict.DictMixin.contains_key] alias when working with
        *dict-like* objects to be self-documenting.

    See Also:
        [`contains_ignoring_case()`][assertpy2.string.StringMixin.contains_ignoring_case] -
            for case-insensitive string contains
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    # membership is tested once per argument, so a one-shot iterator has to be drained first
    values = searchable(self.val)
    if not is_searchable(values):
        # left to `in`, Python answers "argument of type 'int' is not a container or iterable":
        # true, and about the operator rather than about the value the assertion was handed
        refuse(self.val, "a container or iterable")
    if len(items) == 1:
        item = items[0]
        if _is_matcher(item):
            if not any(item.matches(value) for value in values):
                diff = DiffResult(
                    kind="contains",
                    entries=[DiffEntry(path="missing", actual=None, absent="actual", expected=item.describe())],
                )
                return self.error(
                    f"Expected <{values}> to contain item matching {item.describe()}, but did not.",
                    diff=diff,
                )
        elif item not in values:
            if mapping_shaped(values):
                diff = DiffResult(
                    kind="contains",
                    entries=[DiffEntry(path="missing", actual=None, absent="actual", expected=item)],
                )
                return self.error(f"Expected <{values}> to contain key <{item}>, but did not.", diff=diff)
            closest = self._closest_element(item, values)
            if closest is not None:
                element, entries = closest
                return self.error(
                    f"Expected <{values}> to contain item <{item}>, but did not."
                    f" Closest element <{element}> differs at {self._fmt_closest(entries)}.",
                    diff=DiffResult(kind="contains", entries=entries),
                )
            diff = DiffResult(
                kind="contains", entries=[DiffEntry(path="missing", actual=None, absent="actual", expected=item)]
            )
            return self.error(f"Expected <{values}> to contain item <{item}>, but did not.", diff=diff)
    else:
        missing = missing_items(values, items, _is_matcher)
        if missing:
            missing_desc = [
                missing_item.describe() if _is_matcher(missing_item) else missing_item for missing_item in missing
            ]
            diff = DiffResult(
                kind="contains",
                entries=[
                    DiffEntry(path="missing", actual=None, absent="actual", expected=missing_item)
                    for missing_item in missing_desc
                ],
            )
            if mapping_shaped(values):
                return self.error(
                    f"Expected <{values}> to contain keys {self._fmt_items(items)}, but did not contain"
                    f" key{'' if len(missing) == 1 else 's'} {self._fmt_items(missing_desc)}.",
                    diff=diff,
                )
            else:
                return self.error(
                    f"Expected <{values}> to contain items {self._fmt_items(items)},"
                    f" but did not contain {self._fmt_items(missing_desc)}.",
                    diff=diff,
                )
    return self

does_not_contain

does_not_contain(*items: object) -> Self

Asserts that val does not contain the given item or items.

Checks if the collection excludes the given item or items using in operator.

Parameters:

Name Type Description Default
*items object

the item or items expected to be excluded

()

Examples:

Usage:

assert_that('foo').does_not_contain('x')
assert_that(['a', 'b']).does_not_contain('x', 'y')
assert_that((1, 2, 3)).does_not_contain(4, 5)
assert_that({'a': 1, 'b': 2}).does_not_contain('x', 'y')  # checks keys
assert_that({'a', 'b'}).does_not_contain('x', 'y')
assert_that([1, 2, 3]).is_type_of(list).contains(1, 2).does_not_contain(4, 5)

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val does contain the item or items

Note

Accepts a Matcher for any item, the same as contains(): the assertion then fails when any item of val matches it.

Tip

Use the does_not_contain_key() alias when working with dict-like objects to be self-documenting.

Source code in assertpy2/contains.py
def does_not_contain(self, *items: object) -> Self:
    """Asserts that val does not contain the given item or items.

    Checks if the collection excludes the given item or items using ``in`` operator.

    Args:
        *items: the item or items expected to be excluded

    Examples:
        Usage:

            assert_that('foo').does_not_contain('x')
            assert_that(['a', 'b']).does_not_contain('x', 'y')
            assert_that((1, 2, 3)).does_not_contain(4, 5)
            assert_that({'a': 1, 'b': 2}).does_not_contain('x', 'y')  # checks keys
            assert_that({'a', 'b'}).does_not_contain('x', 'y')
            assert_that([1, 2, 3]).is_type_of(list).contains(1, 2).does_not_contain(4, 5)

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val **does** contain the item or items

    Note:
        Accepts a `Matcher` for any item, the same as
        [`contains()`][assertpy2.contains.ContainsMixin.contains]: the assertion then fails when any
        item of val matches it.

    Tip:
        Use the [`does_not_contain_key()`][assertpy2.dict.DictMixin.does_not_contain_key] alias when working with
        *dict-like* objects to be self-documenting.
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    values = materialized(self.val)

    def described(item: object) -> object:
        return item.describe() if _is_matcher(item) else item

    def present(item: object) -> bool:
        # `contains` has accepted a matcher since it was written and this, its own negation, did
        # not: a matcher handed here was compared with `in`, which asks the wrong question
        if _is_matcher(item):
            return any(item.matches(value) for value in values)
        return item in values

    if len(items) == 1:
        if present(items[0]):
            return self.error(f"Expected <{values}> to not contain item <{described(items[0])}>, but did.")
    else:
        found = [item for item in items if present(item)]
        if found:
            shown = [described(item) for item in items]
            found_shown = [described(item) for item in found]
            return self.error(
                f"Expected <{values}> to not contain items {self._fmt_items(shown)},"
                f" but did contain {self._fmt_items(found_shown)}."
            )
    return self

contains_only

contains_only(*items: object) -> Self

Asserts that val contains only the given item or items.

Checks if the collection contains only the given item or items using in operator.

Parameters:

Name Type Description Default
*items object

the only item or items expected to be contained

()

Examples:

Usage:

assert_that('foo').contains_only('f', 'o')
assert_that(['a', 'a', 'b']).contains_only('a', 'b')
assert_that((1, 1, 2)).contains_only(1, 2)
assert_that({'a': 1, 'a': 2, 'b': 3}).contains_only('a', 'b')
assert_that({'a', 'a', 'b'}).contains_only('a', 'b')

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val contains anything not item or items

Source code in assertpy2/contains.py
def contains_only(self, *items: object) -> Self:
    """Asserts that val contains *only* the given item or items.

    Checks if the collection contains only the given item or items using ``in`` operator.

    Args:
        *items: the *only* item or items expected to be contained

    Examples:
        Usage:

            assert_that('foo').contains_only('f', 'o')
            assert_that(['a', 'a', 'b']).contains_only('a', 'b')
            assert_that((1, 1, 2)).contains_only(1, 2)
            assert_that({'a': 1, 'a': 2, 'b': 3}).contains_only('a', 'b')
            assert_that({'a', 'a', 'b'}).contains_only('a', 'b')

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val contains anything **not** item or items
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    # walked twice below and rendered a third time, so a one-shot iterator has to be drained
    values = searchable(self.val)
    if not is_walkable(values):
        # "only these" has to see every element, so answering `in` is not enough here: left to the
        # comprehension, a value like that came back as Python's "object is not iterable"
        refuse(self.val, "iterable")
    extra, missing = only_faults(values, items)
    if extra or missing:
        # both halves at once: reporting only the extras sends the reader to fix one problem and
        # rerun into the other, and the message wording of each half alone is unchanged
        faults = []
        entries = []
        if extra:
            faults.append(f"did contain {self._fmt_items(extra)}")
            entries += [DiffEntry(path="extra", actual=item, expected=None, absent="expected") for item in extra]
        if missing:
            faults.append(f"did not contain {self._fmt_items(missing)}")
            entries += [DiffEntry(path="missing", actual=None, absent="actual", expected=item) for item in missing]
        return self.error(
            f"Expected <{values}> to contain only {self._fmt_items(items)}, but {' and '.join(faults)}.",
            diff=DiffResult(kind="contains", entries=entries),
        )
    return self

contains_sequence

contains_sequence(*items: object) -> Self

Asserts that val contains the given ordered sequence of items.

Checks if the collection contains the given sequence of items using in operator.

Parameters:

Name Type Description Default
*items object

the sequence of items expected to be contained

()

Examples:

Usage:

assert_that('foo').contains_sequence('f', 'o')
assert_that('foo').contains_sequence('o', 'o')
assert_that(['a', 'b', 'c']).contains_sequence('b', 'c')
assert_that((1, 2, 3)).contains_sequence(1, 2)

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val does not contains the given sequence of items

Source code in assertpy2/contains.py
def contains_sequence(self, *items: object) -> Self:
    """Asserts that val contains the given ordered sequence of items.

    Checks if the collection contains the given sequence of items using ``in`` operator.

    Args:
        *items: the sequence of items expected to be contained

    Examples:
        Usage:

            assert_that('foo').contains_sequence('f', 'o')
            assert_that('foo').contains_sequence('o', 'o')
            assert_that(['a', 'b', 'c']).contains_sequence('b', 'c')
            assert_that((1, 2, 3)).contains_sequence(1, 2)

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val does **not** contains the given sequence of items
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    if isinstance(self.val, str):
        search_start = 0
        for item in items:
            text = require_type(item, str, "a string, to match val", subject=argument("item"))
            found_index = self.val.find(text, search_start)
            if found_index == -1:
                # name where the chain broke: "but did not" makes the reader re-derive it by eye
                matched = items[: items.index(item)]
                trail = f" after {self._fmt_items(matched)}" if matched else ""
                return self.error(
                    f"Expected <{self.val}> to contain sequence {self._fmt_items(items)}, but <{item}>"
                    f" was not found{trail}."
                )
            search_start = found_index + len(text)
        return self
    best_prefix = 0
    # this walk is by index, which a one-shot iterator does not support at all
    values = materialized(self.val)
    if not isinstance(values, Sequence):
        # two different wrong inputs, and the old guard reported both as "not iterable": true for
        # an int, plainly false for a set, which is iterable and simply has no order to hold a
        # sequence in
        require_type(values, Iterable, "iterable")
        refuse(self.val, "a sequence, to contain a sequence")
    for i in range(len(values) - len(items) + 1):
        for j in range(len(items)):
            if values[i + j] != items[j]:
                best_prefix = max(best_prefix, j)
                break
        else:
            return self
    # the longest run that lined up says where the sequence broke down
    detail = (
        f" The longest run that matched was {self._fmt_items(items[:best_prefix])}."
        if best_prefix
        # not "no element equals X": X may well be present, just never at a position where the
        # whole sequence still fits
        else f" No run started with <{items[0]}>."
    )
    return self.error(f"Expected <{values}> to contain sequence {self._fmt_items(items)}, but did not.{detail}")

contains_duplicates

contains_duplicates() -> Self

Asserts that val is iterable and does contain duplicates.

Examples:

Usage:

assert_that('foo').contains_duplicates()
assert_that(['a', 'a', 'b']).contains_duplicates()
assert_that((1, 1, 2)).contains_duplicates()

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val does not contain any duplicates

Source code in assertpy2/contains.py
def contains_duplicates(self) -> Self:
    """Asserts that val is iterable and *does* contain duplicates.

    Examples:
        Usage:

            assert_that('foo').contains_duplicates()
            assert_that(['a', 'a', 'b']).contains_duplicates()
            assert_that((1, 1, 2)).contains_duplicates()

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val does **not** contain any duplicates
    """
    try:
        values = list(self.val)
    except TypeError:
        refuse(self.val, "iterable")
    if has_duplicates(values):
        return self
    return self.error(f"Expected <{self.val}> to contain duplicates, but did not.")

does_not_contain_duplicates

does_not_contain_duplicates() -> Self

Asserts that val is iterable and does not contain any duplicates.

Examples:

Usage:

assert_that('fox').does_not_contain_duplicates()
assert_that(['a', 'b', 'c']).does_not_contain_duplicates()
assert_that((1, 2, 3)).does_not_contain_duplicates()

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val does contain duplicates

Source code in assertpy2/contains.py
def does_not_contain_duplicates(self) -> Self:
    """Asserts that val is iterable and *does not* contain any duplicates.

    Examples:
        Usage:

            assert_that('fox').does_not_contain_duplicates()
            assert_that(['a', 'b', 'c']).does_not_contain_duplicates()
            assert_that((1, 2, 3)).does_not_contain_duplicates()

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val **does** contain duplicates
    """
    try:
        values = list(self.val)
    except TypeError:
        refuse(self.val, "iterable")
    if not has_duplicates(values):
        return self
    # name them: "but did" leaves the reader to scan the value for the repeat, and the sibling
    # contains_only_once already reports exactly this shape
    repeated = repeated_items(values)
    return self.error(
        f"Expected <{self.val}> to not contain duplicates, but {self._fmt_items(repeated)}"
        f" {'was' if len(repeated) == 1 else 'were'} repeated.",
        diff=DiffResult(
            kind="contains",
            entries=[
                DiffEntry(path="duplicated", actual=values.count(value), expected=value) for value in repeated
            ],
        ),
    )

is_empty

is_empty() -> Self

Asserts that val is empty.

Examples:

Usage:

assert_that('').is_empty()
assert_that([]).is_empty()
assert_that(()).is_empty()
assert_that({}).is_empty()
assert_that(set()).is_empty()

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val is not empty

Source code in assertpy2/contains.py
def is_empty(self) -> Self:
    """Asserts that val is empty.

    Examples:
        Usage:

            assert_that('').is_empty()
            assert_that([]).is_empty()
            assert_that(()).is_empty()
            assert_that({}).is_empty()
            assert_that(set()).is_empty()

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val is **not** empty
    """
    if sized_len(self.val) != 0:
        if isinstance(self.val, str):
            return self.error(f"Expected <{self.val}> to be empty string, but was not.")
        else:
            return self.error(f"Expected <{self.val}> to be empty, but was not.")
    return self

is_not_empty

is_not_empty() -> Self

Asserts that val is not empty.

Examples:

Usage:

assert_that('foo').is_not_empty()
assert_that(['a', 'b']).is_not_empty()
assert_that((1, 2, 3)).is_not_empty()
assert_that({'a': 1, 'b': 2}).is_not_empty()
assert_that({'a', 'b'}).is_not_empty()

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val is empty

Source code in assertpy2/contains.py
def is_not_empty(self) -> Self:
    """Asserts that val is *not* empty.

    Examples:
        Usage:

            assert_that('foo').is_not_empty()
            assert_that(['a', 'b']).is_not_empty()
            assert_that((1, 2, 3)).is_not_empty()
            assert_that({'a': 1, 'b': 2}).is_not_empty()
            assert_that({'a', 'b'}).is_not_empty()

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val **is** empty
    """
    if sized_len(self.val) == 0:
        if isinstance(self.val, str):
            return self.error("Expected not empty string, but was empty.")
        else:
            return self.error("Expected not empty, but was empty.")
    return self

contains_exactly

contains_exactly(*items: object) -> Self

Asserts that val contains exactly the given items in the given order.

Unlike contains_only() (which ignores order) and contains_sequence() (which allows extra items), this method requires exact count, items, and order.

Parameters:

Name Type Description Default
*items object

the items expected, in exact order

()

Examples:

Usage:

assert_that([1, 2, 3]).contains_exactly(1, 2, 3)
assert_that(['a', 'b']).contains_exactly('a', 'b')

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val does not contain exactly the given items in order

Source code in assertpy2/contains.py
def contains_exactly(self, *items: object) -> Self:
    """Asserts that val contains exactly the given items in the given order.

    Unlike [`contains_only()`][assertpy2.contains.ContainsMixin.contains_only] (which ignores
    order) and [`contains_sequence()`][assertpy2.contains.ContainsMixin.contains_sequence]
    (which allows extra items), this method requires exact count, items, and order.

    Args:
        *items: the items expected, in exact order

    Examples:
        Usage:

            assert_that([1, 2, 3]).contains_exactly(1, 2, 3)
            assert_that(['a', 'b']).contains_exactly('a', 'b')

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val does **not** contain exactly the given items in order
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    try:
        val_list = list(self.val)
    except TypeError:
        refuse(self.val, "iterable")
    expected_list = list(items)
    if val_list != expected_list:
        message = f"Expected <{self.val}> to contain exactly {self._fmt_items(items)}, but did not."
        entries = _multiset_diff_entries(val_list, expected_list)
        if entries:
            diff = DiffResult(kind="contains", entries=entries)
        else:
            # equal multisets, so only the order differs: name the first position that disagrees,
            # which is the one the reader has to look at anyway
            pairs = enumerate(zip(val_list, expected_list, strict=True))  # equal multisets, equal lengths
            index = next(i for i, (found, wanted) in pairs if found != wanted)
            message += f" Same items, but the order differs at index {index}."
            diff = DiffResult(
                kind="sequence",
                entries=[_ROOT.index(index).entry(actual=val_list[index], expected=expected_list[index])],
            )
        return self.error(message, diff=diff)
    return self

contains_exactly_in_any_order

contains_exactly_in_any_order(*items: object) -> Self

Asserts that val contains exactly the given items, in any order.

Like contains_exactly() but ignoring order: val and the given items must be equal as multisets, so duplicates count (each item must occur exactly as many times as given). Unlike contains_only() (which checks membership both ways and ignores counts), an extra duplicate or a missing one fails.

Parameters:

Name Type Description Default
*items object

the items expected, in any order

()

Examples:

Usage:

assert_that([3, 1, 2]).contains_exactly_in_any_order(1, 2, 3)
assert_that(['b', 'a', 'b']).contains_exactly_in_any_order('a', 'b', 'b')

assert_that([1, 2, 2]).contains_exactly_in_any_order(1, 2)  # fails (extra 2)
assert_that([1, 2]).contains_exactly_in_any_order(1, 2, 2)  # fails (missing 2)

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val does not contain exactly the given items in any order

TypeError

if val is not iterable

ValueError

if no items are given

Source code in assertpy2/contains.py
def contains_exactly_in_any_order(self, *items: object) -> Self:
    """Asserts that val contains exactly the given items, in any order.

    Like [`contains_exactly()`][assertpy2.contains.ContainsMixin.contains_exactly] but ignoring
    order: val and the given items must be equal as multisets, so duplicates count (each item
    must occur exactly as many times as given).  Unlike
    [`contains_only()`][assertpy2.contains.ContainsMixin.contains_only] (which checks membership
    both ways and ignores counts), an extra duplicate or a missing one fails.

    Args:
        *items: the items expected, in any order

    Examples:
        Usage:

            assert_that([3, 1, 2]).contains_exactly_in_any_order(1, 2, 3)
            assert_that(['b', 'a', 'b']).contains_exactly_in_any_order('a', 'b', 'b')

            assert_that([1, 2, 2]).contains_exactly_in_any_order(1, 2)  # fails (extra 2)
            assert_that([1, 2]).contains_exactly_in_any_order(1, 2, 2)  # fails (missing 2)

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val does **not** contain exactly the given items in any order
        TypeError: if val is not iterable
        ValueError: if no items are given
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    try:
        val_list = list(self.val)
    except TypeError:
        refuse(self.val, "iterable")
    entries = _multiset_diff_entries(val_list, list(items))
    if entries:
        return self.error(
            f"Expected <{self.val}> to contain exactly {self._fmt_items(items)} in any order, but did not.",
            diff=DiffResult(kind="contains", entries=entries),
        )
    return self

contains_in_order

contains_in_order(*items: object) -> Self

Asserts that val contains the given items in the given order (as a subsequence).

Items must appear in the given order but do not need to be contiguous. Unlike contains_sequence() which requires contiguous items.

Parameters:

Name Type Description Default
*items object

the items expected, in order (but not necessarily contiguous)

()

Examples:

Usage:

assert_that([1, 5, 2, 8, 3]).contains_in_order(1, 2, 3)
assert_that(['a', 'x', 'b', 'y', 'c']).contains_in_order('a', 'b', 'c')

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val does not contain items in the given order

Source code in assertpy2/contains.py
def contains_in_order(self, *items: object) -> Self:
    """Asserts that val contains the given items in the given order (as a subsequence).

    Items must appear in the given order but do not need to be contiguous.
    Unlike [`contains_sequence()`][assertpy2.contains.ContainsMixin.contains_sequence] which
    requires contiguous items.

    Args:
        *items: the items expected, in order (but not necessarily contiguous)

    Examples:
        Usage:

            assert_that([1, 5, 2, 8, 3]).contains_in_order(1, 2, 3)
            assert_that(['a', 'x', 'b', 'y', 'c']).contains_in_order('a', 'b', 'c')

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val does **not** contain items in the given order
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    try:
        val_list = list(self.val)
    except TypeError:
        refuse(self.val, "iterable")
    item_index = 0
    for element in val_list:
        if item_index < len(items) and element == items[item_index]:
            item_index += 1
    if item_index != len(items):
        # item_index counts how many lined up before the run stopped, so the next one is the culprit
        matched = items[:item_index]
        trail = f" after {self._fmt_items(matched)}" if matched else ""
        return self.error(
            f"Expected <{self.val}> to contain {self._fmt_items(items)} in order, but <{items[item_index]}>"
            f" did not follow{trail}."
        )
    return self

contains_only_once

contains_only_once(*items: object) -> Self

Asserts that val contains each given item exactly once.

Each given item must appear in val with a count of exactly one: an item absent from val is reported as missing, an item occurring more than once is reported as duplicated.

Parameters:

Name Type Description Default
*items object

the items each expected to occur exactly once

()

Examples:

Usage:

assert_that([1, 2, 3]).contains_only_once(1, 3)
assert_that('foo').contains_only_once('f')

assert_that([1, 2, 2, 3]).contains_only_once(2)  # fails (occurs twice)
assert_that([1, 2, 3]).contains_only_once(4)  # fails (missing)

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if any given item is missing from val or occurs more than once

TypeError

if val is not iterable

ValueError

if no items are given

Source code in assertpy2/contains.py
def contains_only_once(self, *items: object) -> Self:
    """Asserts that val contains each given item exactly once.

    Each given item must appear in val with a count of exactly one: an item absent from val is
    reported as missing, an item occurring more than once is reported as duplicated.

    Args:
        *items: the items each expected to occur exactly once

    Examples:
        Usage:

            assert_that([1, 2, 3]).contains_only_once(1, 3)
            assert_that('foo').contains_only_once('f')

            assert_that([1, 2, 2, 3]).contains_only_once(2)  # fails (occurs twice)
            assert_that([1, 2, 3]).contains_only_once(4)  # fails (missing)

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if any given item is missing from val or occurs more than once
        TypeError: if val is not iterable
        ValueError: if no items are given
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    try:
        val_list = list(materialized(self.val))
    except TypeError:
        refuse(self.val, "iterable")
    # list.count compares with == so unhashable items (dicts, lists) work, unlike Counter/hashing
    missing = [item for item in items if val_list.count(item) == 0]
    duplicated = [item for item in items if val_list.count(item) > 1]
    if missing or duplicated:
        entries = [DiffEntry(path="missing", actual=None, absent="actual", expected=item) for item in missing]
        entries.extend(
            DiffEntry(path="duplicated", actual=val_list.count(item), expected=item) for item in duplicated
        )
        problems = []
        if missing:
            problems.append(f"did not contain {self._fmt_items(missing)}")
        if duplicated:
            problems.append(f"contained {self._fmt_items(duplicated)} more than once")
        return self.error(
            f"Expected <{val_list}> to contain {self._fmt_items(items)} only once, but {' and '.join(problems)}.",
            diff=DiffResult(kind="contains", entries=entries),
        )
    return self

is_in

is_in(*items: object) -> Self

Asserts that val is equal to one of the given items.

Parameters:

Name Type Description Default
*items object

the items expected to contain val

()

Examples:

Usage:

assert_that('foo').is_in('foo', 'bar', 'baz')
assert_that(1).is_in(0, 1, 2, 3)

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val is not in the given items

Source code in assertpy2/contains.py
def is_in(self, *items: object) -> Self:
    """Asserts that val is equal to one of the given items.

    Args:
        *items: the items expected to contain val

    Examples:
        Usage:

            assert_that('foo').is_in('foo', 'bar', 'baz')
            assert_that(1).is_in(0, 1, 2, 3)

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val is **not** in the given items
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    else:
        for item in items:
            if self.val == item:
                return self
    return self.error(f"Expected <{self.val}> to be in {self._fmt_items(items)}, but was not.")

is_not_in

is_not_in(*items: object) -> Self

Asserts that val is not equal to one of the given items.

Parameters:

Name Type Description Default
*items object

the items expected to exclude val

()

Examples:

Usage:

assert_that('foo').is_not_in('bar', 'baz', 'box')
assert_that(1).is_not_in(-1, -2, -3)

Returns:

Name Type Description
AssertionBuilder Self

returns this instance to chain to the next assertion

Raises:

Type Description
AssertionError

if val is in the given items

Source code in assertpy2/contains.py
def is_not_in(self, *items: object) -> Self:
    """Asserts that val is not equal to one of the given items.

    Args:
        *items: the items expected to exclude val

    Examples:
        Usage:

            assert_that('foo').is_not_in('bar', 'baz', 'box')
            assert_that(1).is_not_in(-1, -2, -3)

    Returns:
        AssertionBuilder: returns this instance to chain to the next assertion

    Raises:
        AssertionError: if val **is** in the given items
    """
    if len(items) == 0:
        raise ValueError("one or more args must be given")
    else:
        for item in items:
            if self.val == item:
                return self.error(f"Expected <{self.val}> to not be in {self._fmt_items(items)}, but was.")
    return self