1014 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			1014 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			Python
		
	
	
	
import math
 | 
						|
import pprint
 | 
						|
from collections.abc import Collection
 | 
						|
from collections.abc import Sized
 | 
						|
from decimal import Decimal
 | 
						|
from numbers import Complex
 | 
						|
from types import TracebackType
 | 
						|
from typing import Any
 | 
						|
from typing import Callable
 | 
						|
from typing import cast
 | 
						|
from typing import ContextManager
 | 
						|
from typing import final
 | 
						|
from typing import List
 | 
						|
from typing import Mapping
 | 
						|
from typing import Optional
 | 
						|
from typing import overload
 | 
						|
from typing import Pattern
 | 
						|
from typing import Sequence
 | 
						|
from typing import Tuple
 | 
						|
from typing import Type
 | 
						|
from typing import TYPE_CHECKING
 | 
						|
from typing import TypeVar
 | 
						|
from typing import Union
 | 
						|
 | 
						|
import _pytest._code
 | 
						|
from _pytest.compat import STRING_TYPES
 | 
						|
from _pytest.outcomes import fail
 | 
						|
 | 
						|
if TYPE_CHECKING:
 | 
						|
    from numpy import ndarray
 | 
						|
 | 
						|
 | 
						|
def _compare_approx(
 | 
						|
    full_object: object,
 | 
						|
    message_data: Sequence[Tuple[str, str, str]],
 | 
						|
    number_of_elements: int,
 | 
						|
    different_ids: Sequence[object],
 | 
						|
    max_abs_diff: float,
 | 
						|
    max_rel_diff: float,
 | 
						|
) -> List[str]:
 | 
						|
    message_list = list(message_data)
 | 
						|
    message_list.insert(0, ("Index", "Obtained", "Expected"))
 | 
						|
    max_sizes = [0, 0, 0]
 | 
						|
    for index, obtained, expected in message_list:
 | 
						|
        max_sizes[0] = max(max_sizes[0], len(index))
 | 
						|
        max_sizes[1] = max(max_sizes[1], len(obtained))
 | 
						|
        max_sizes[2] = max(max_sizes[2], len(expected))
 | 
						|
    explanation = [
 | 
						|
        f"comparison failed. Mismatched elements: {len(different_ids)} / {number_of_elements}:",
 | 
						|
        f"Max absolute difference: {max_abs_diff}",
 | 
						|
        f"Max relative difference: {max_rel_diff}",
 | 
						|
    ] + [
 | 
						|
        f"{indexes:<{max_sizes[0]}} | {obtained:<{max_sizes[1]}} | {expected:<{max_sizes[2]}}"
 | 
						|
        for indexes, obtained, expected in message_list
 | 
						|
    ]
 | 
						|
    return explanation
 | 
						|
 | 
						|
 | 
						|
# builtin pytest.approx helper
 | 
						|
 | 
						|
 | 
						|
class ApproxBase:
 | 
						|
    """Provide shared utilities for making approximate comparisons between
 | 
						|
    numbers or sequences of numbers."""
 | 
						|
 | 
						|
    # Tell numpy to use our `__eq__` operator instead of its.
 | 
						|
    __array_ufunc__ = None
 | 
						|
    __array_priority__ = 100
 | 
						|
 | 
						|
    def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None:
 | 
						|
        __tracebackhide__ = True
 | 
						|
        self.expected = expected
 | 
						|
        self.abs = abs
 | 
						|
        self.rel = rel
 | 
						|
        self.nan_ok = nan_ok
 | 
						|
        self._check_type()
 | 
						|
 | 
						|
    def __repr__(self) -> str:
 | 
						|
        raise NotImplementedError
 | 
						|
 | 
						|
    def _repr_compare(self, other_side: Any) -> List[str]:
 | 
						|
        return [
 | 
						|
            "comparison failed",
 | 
						|
            f"Obtained: {other_side}",
 | 
						|
            f"Expected: {self}",
 | 
						|
        ]
 | 
						|
 | 
						|
    def __eq__(self, actual) -> bool:
 | 
						|
        return all(
 | 
						|
            a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual)
 | 
						|
        )
 | 
						|
 | 
						|
    def __bool__(self):
 | 
						|
        __tracebackhide__ = True
 | 
						|
        raise AssertionError(
 | 
						|
            "approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?"
 | 
						|
        )
 | 
						|
 | 
						|
    # Ignore type because of https://github.com/python/mypy/issues/4266.
 | 
						|
    __hash__ = None  # type: ignore
 | 
						|
 | 
						|
    def __ne__(self, actual) -> bool:
 | 
						|
        return not (actual == self)
 | 
						|
 | 
						|
    def _approx_scalar(self, x) -> "ApproxScalar":
 | 
						|
        if isinstance(x, Decimal):
 | 
						|
            return ApproxDecimal(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
 | 
						|
        return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
 | 
						|
 | 
						|
    def _yield_comparisons(self, actual):
 | 
						|
        """Yield all the pairs of numbers to be compared.
 | 
						|
 | 
						|
        This is used to implement the `__eq__` method.
 | 
						|
        """
 | 
						|
        raise NotImplementedError
 | 
						|
 | 
						|
    def _check_type(self) -> None:
 | 
						|
        """Raise a TypeError if the expected value is not a valid type."""
 | 
						|
        # This is only a concern if the expected value is a sequence.  In every
 | 
						|
        # other case, the approx() function ensures that the expected value has
 | 
						|
        # a numeric type.  For this reason, the default is to do nothing.  The
 | 
						|
        # classes that deal with sequences should reimplement this method to
 | 
						|
        # raise if there are any non-numeric elements in the sequence.
 | 
						|
 | 
						|
 | 
						|
def _recursive_sequence_map(f, x):
 | 
						|
    """Recursively map a function over a sequence of arbitrary depth"""
 | 
						|
    if isinstance(x, (list, tuple)):
 | 
						|
        seq_type = type(x)
 | 
						|
        return seq_type(_recursive_sequence_map(f, xi) for xi in x)
 | 
						|
    else:
 | 
						|
        return f(x)
 | 
						|
 | 
						|
 | 
						|
class ApproxNumpy(ApproxBase):
 | 
						|
    """Perform approximate comparisons where the expected value is numpy array."""
 | 
						|
 | 
						|
    def __repr__(self) -> str:
 | 
						|
        list_scalars = _recursive_sequence_map(
 | 
						|
            self._approx_scalar, self.expected.tolist()
 | 
						|
        )
 | 
						|
        return f"approx({list_scalars!r})"
 | 
						|
 | 
						|
    def _repr_compare(self, other_side: "ndarray") -> List[str]:
 | 
						|
        import itertools
 | 
						|
        import math
 | 
						|
 | 
						|
        def get_value_from_nested_list(
 | 
						|
            nested_list: List[Any], nd_index: Tuple[Any, ...]
 | 
						|
        ) -> Any:
 | 
						|
            """
 | 
						|
            Helper function to get the value out of a nested list, given an n-dimensional index.
 | 
						|
            This mimics numpy's indexing, but for raw nested python lists.
 | 
						|
            """
 | 
						|
            value: Any = nested_list
 | 
						|
            for i in nd_index:
 | 
						|
                value = value[i]
 | 
						|
            return value
 | 
						|
 | 
						|
        np_array_shape = self.expected.shape
 | 
						|
        approx_side_as_seq = _recursive_sequence_map(
 | 
						|
            self._approx_scalar, self.expected.tolist()
 | 
						|
        )
 | 
						|
 | 
						|
        if np_array_shape != other_side.shape:
 | 
						|
            return [
 | 
						|
                "Impossible to compare arrays with different shapes.",
 | 
						|
                f"Shapes: {np_array_shape} and {other_side.shape}",
 | 
						|
            ]
 | 
						|
 | 
						|
        number_of_elements = self.expected.size
 | 
						|
        max_abs_diff = -math.inf
 | 
						|
        max_rel_diff = -math.inf
 | 
						|
        different_ids = []
 | 
						|
        for index in itertools.product(*(range(i) for i in np_array_shape)):
 | 
						|
            approx_value = get_value_from_nested_list(approx_side_as_seq, index)
 | 
						|
            other_value = get_value_from_nested_list(other_side, index)
 | 
						|
            if approx_value != other_value:
 | 
						|
                abs_diff = abs(approx_value.expected - other_value)
 | 
						|
                max_abs_diff = max(max_abs_diff, abs_diff)
 | 
						|
                if other_value == 0.0:
 | 
						|
                    max_rel_diff = math.inf
 | 
						|
                else:
 | 
						|
                    max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value))
 | 
						|
                different_ids.append(index)
 | 
						|
 | 
						|
        message_data = [
 | 
						|
            (
 | 
						|
                str(index),
 | 
						|
                str(get_value_from_nested_list(other_side, index)),
 | 
						|
                str(get_value_from_nested_list(approx_side_as_seq, index)),
 | 
						|
            )
 | 
						|
            for index in different_ids
 | 
						|
        ]
 | 
						|
        return _compare_approx(
 | 
						|
            self.expected,
 | 
						|
            message_data,
 | 
						|
            number_of_elements,
 | 
						|
            different_ids,
 | 
						|
            max_abs_diff,
 | 
						|
            max_rel_diff,
 | 
						|
        )
 | 
						|
 | 
						|
    def __eq__(self, actual) -> bool:
 | 
						|
        import numpy as np
 | 
						|
 | 
						|
        # self.expected is supposed to always be an array here.
 | 
						|
 | 
						|
        if not np.isscalar(actual):
 | 
						|
            try:
 | 
						|
                actual = np.asarray(actual)
 | 
						|
            except Exception as e:
 | 
						|
                raise TypeError(f"cannot compare '{actual}' to numpy.ndarray") from e
 | 
						|
 | 
						|
        if not np.isscalar(actual) and actual.shape != self.expected.shape:
 | 
						|
            return False
 | 
						|
 | 
						|
        return super().__eq__(actual)
 | 
						|
 | 
						|
    def _yield_comparisons(self, actual):
 | 
						|
        import numpy as np
 | 
						|
 | 
						|
        # `actual` can either be a numpy array or a scalar, it is treated in
 | 
						|
        # `__eq__` before being passed to `ApproxBase.__eq__`, which is the
 | 
						|
        # only method that calls this one.
 | 
						|
 | 
						|
        if np.isscalar(actual):
 | 
						|
            for i in np.ndindex(self.expected.shape):
 | 
						|
                yield actual, self.expected[i].item()
 | 
						|
        else:
 | 
						|
            for i in np.ndindex(self.expected.shape):
 | 
						|
                yield actual[i].item(), self.expected[i].item()
 | 
						|
 | 
						|
 | 
						|
class ApproxMapping(ApproxBase):
 | 
						|
    """Perform approximate comparisons where the expected value is a mapping
 | 
						|
    with numeric values (the keys can be anything)."""
 | 
						|
 | 
						|
    def __repr__(self) -> str:
 | 
						|
        return "approx({!r})".format(
 | 
						|
            {k: self._approx_scalar(v) for k, v in self.expected.items()}
 | 
						|
        )
 | 
						|
 | 
						|
    def _repr_compare(self, other_side: Mapping[object, float]) -> List[str]:
 | 
						|
        import math
 | 
						|
 | 
						|
        approx_side_as_map = {
 | 
						|
            k: self._approx_scalar(v) for k, v in self.expected.items()
 | 
						|
        }
 | 
						|
 | 
						|
        number_of_elements = len(approx_side_as_map)
 | 
						|
        max_abs_diff = -math.inf
 | 
						|
        max_rel_diff = -math.inf
 | 
						|
        different_ids = []
 | 
						|
        for (approx_key, approx_value), other_value in zip(
 | 
						|
            approx_side_as_map.items(), other_side.values()
 | 
						|
        ):
 | 
						|
            if approx_value != other_value:
 | 
						|
                if approx_value.expected is not None and other_value is not None:
 | 
						|
                    max_abs_diff = max(
 | 
						|
                        max_abs_diff, abs(approx_value.expected - other_value)
 | 
						|
                    )
 | 
						|
                    if approx_value.expected == 0.0:
 | 
						|
                        max_rel_diff = math.inf
 | 
						|
                    else:
 | 
						|
                        max_rel_diff = max(
 | 
						|
                            max_rel_diff,
 | 
						|
                            abs(
 | 
						|
                                (approx_value.expected - other_value)
 | 
						|
                                / approx_value.expected
 | 
						|
                            ),
 | 
						|
                        )
 | 
						|
                different_ids.append(approx_key)
 | 
						|
 | 
						|
        message_data = [
 | 
						|
            (str(key), str(other_side[key]), str(approx_side_as_map[key]))
 | 
						|
            for key in different_ids
 | 
						|
        ]
 | 
						|
 | 
						|
        return _compare_approx(
 | 
						|
            self.expected,
 | 
						|
            message_data,
 | 
						|
            number_of_elements,
 | 
						|
            different_ids,
 | 
						|
            max_abs_diff,
 | 
						|
            max_rel_diff,
 | 
						|
        )
 | 
						|
 | 
						|
    def __eq__(self, actual) -> bool:
 | 
						|
        try:
 | 
						|
            if set(actual.keys()) != set(self.expected.keys()):
 | 
						|
                return False
 | 
						|
        except AttributeError:
 | 
						|
            return False
 | 
						|
 | 
						|
        return super().__eq__(actual)
 | 
						|
 | 
						|
    def _yield_comparisons(self, actual):
 | 
						|
        for k in self.expected.keys():
 | 
						|
            yield actual[k], self.expected[k]
 | 
						|
 | 
						|
    def _check_type(self) -> None:
 | 
						|
        __tracebackhide__ = True
 | 
						|
        for key, value in self.expected.items():
 | 
						|
            if isinstance(value, type(self.expected)):
 | 
						|
                msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n  full mapping={}"
 | 
						|
                raise TypeError(msg.format(key, value, pprint.pformat(self.expected)))
 | 
						|
 | 
						|
 | 
						|
class ApproxSequenceLike(ApproxBase):
 | 
						|
    """Perform approximate comparisons where the expected value is a sequence of numbers."""
 | 
						|
 | 
						|
    def __repr__(self) -> str:
 | 
						|
        seq_type = type(self.expected)
 | 
						|
        if seq_type not in (tuple, list):
 | 
						|
            seq_type = list
 | 
						|
        return "approx({!r})".format(
 | 
						|
            seq_type(self._approx_scalar(x) for x in self.expected)
 | 
						|
        )
 | 
						|
 | 
						|
    def _repr_compare(self, other_side: Sequence[float]) -> List[str]:
 | 
						|
        import math
 | 
						|
 | 
						|
        if len(self.expected) != len(other_side):
 | 
						|
            return [
 | 
						|
                "Impossible to compare lists with different sizes.",
 | 
						|
                f"Lengths: {len(self.expected)} and {len(other_side)}",
 | 
						|
            ]
 | 
						|
 | 
						|
        approx_side_as_map = _recursive_sequence_map(self._approx_scalar, self.expected)
 | 
						|
 | 
						|
        number_of_elements = len(approx_side_as_map)
 | 
						|
        max_abs_diff = -math.inf
 | 
						|
        max_rel_diff = -math.inf
 | 
						|
        different_ids = []
 | 
						|
        for i, (approx_value, other_value) in enumerate(
 | 
						|
            zip(approx_side_as_map, other_side)
 | 
						|
        ):
 | 
						|
            if approx_value != other_value:
 | 
						|
                abs_diff = abs(approx_value.expected - other_value)
 | 
						|
                max_abs_diff = max(max_abs_diff, abs_diff)
 | 
						|
                if other_value == 0.0:
 | 
						|
                    max_rel_diff = math.inf
 | 
						|
                else:
 | 
						|
                    max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value))
 | 
						|
                different_ids.append(i)
 | 
						|
 | 
						|
        message_data = [
 | 
						|
            (str(i), str(other_side[i]), str(approx_side_as_map[i]))
 | 
						|
            for i in different_ids
 | 
						|
        ]
 | 
						|
 | 
						|
        return _compare_approx(
 | 
						|
            self.expected,
 | 
						|
            message_data,
 | 
						|
            number_of_elements,
 | 
						|
            different_ids,
 | 
						|
            max_abs_diff,
 | 
						|
            max_rel_diff,
 | 
						|
        )
 | 
						|
 | 
						|
    def __eq__(self, actual) -> bool:
 | 
						|
        try:
 | 
						|
            if len(actual) != len(self.expected):
 | 
						|
                return False
 | 
						|
        except TypeError:
 | 
						|
            return False
 | 
						|
        return super().__eq__(actual)
 | 
						|
 | 
						|
    def _yield_comparisons(self, actual):
 | 
						|
        return zip(actual, self.expected)
 | 
						|
 | 
						|
    def _check_type(self) -> None:
 | 
						|
        __tracebackhide__ = True
 | 
						|
        for index, x in enumerate(self.expected):
 | 
						|
            if isinstance(x, type(self.expected)):
 | 
						|
                msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n  full sequence: {}"
 | 
						|
                raise TypeError(msg.format(x, index, pprint.pformat(self.expected)))
 | 
						|
 | 
						|
 | 
						|
class ApproxScalar(ApproxBase):
 | 
						|
    """Perform approximate comparisons where the expected value is a single number."""
 | 
						|
 | 
						|
    # Using Real should be better than this Union, but not possible yet:
 | 
						|
    # https://github.com/python/typeshed/pull/3108
 | 
						|
    DEFAULT_ABSOLUTE_TOLERANCE: Union[float, Decimal] = 1e-12
 | 
						|
    DEFAULT_RELATIVE_TOLERANCE: Union[float, Decimal] = 1e-6
 | 
						|
 | 
						|
    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
 | 
						|
        # handle complex numbers, e.g. (inf + 1j).
 | 
						|
        if (not isinstance(self.expected, (Complex, Decimal))) or math.isinf(
 | 
						|
            abs(self.expected)  # type: ignore[arg-type]
 | 
						|
        ):
 | 
						|
            return str(self.expected)
 | 
						|
 | 
						|
        # If a sensible tolerance can't be calculated, self.tolerance will
 | 
						|
        # raise a ValueError.  In this case, display '???'.
 | 
						|
        try:
 | 
						|
            vetted_tolerance = f"{self.tolerance:.1e}"
 | 
						|
            if (
 | 
						|
                isinstance(self.expected, Complex)
 | 
						|
                and self.expected.imag
 | 
						|
                and not math.isinf(self.tolerance)
 | 
						|
            ):
 | 
						|
                vetted_tolerance += " ∠ ±180°"
 | 
						|
        except ValueError:
 | 
						|
            vetted_tolerance = "???"
 | 
						|
 | 
						|
        return f"{self.expected} ± {vetted_tolerance}"
 | 
						|
 | 
						|
    def __eq__(self, actual) -> bool:
 | 
						|
        """Return whether the given value is equal to the expected value
 | 
						|
        within the pre-specified tolerance."""
 | 
						|
        asarray = _as_numpy_array(actual)
 | 
						|
        if asarray is not None:
 | 
						|
            # Call ``__eq__()`` manually to prevent infinite-recursion with
 | 
						|
            # numpy<1.13.  See #3748.
 | 
						|
            return all(self.__eq__(a) for a in asarray.flat)
 | 
						|
 | 
						|
        # Short-circuit exact equality.
 | 
						|
        if actual == self.expected:
 | 
						|
            return True
 | 
						|
 | 
						|
        # If either type is non-numeric, fall back to strict equality.
 | 
						|
        # NB: we need Complex, rather than just Number, to ensure that __abs__,
 | 
						|
        # __sub__, and __float__ are defined.
 | 
						|
        if not (
 | 
						|
            isinstance(self.expected, (Complex, Decimal))
 | 
						|
            and isinstance(actual, (Complex, Decimal))
 | 
						|
        ):
 | 
						|
            return False
 | 
						|
 | 
						|
        # Allow the user to control whether NaNs are considered equal to each
 | 
						|
        # other or not.  The abs() calls are for compatibility with complex
 | 
						|
        # numbers.
 | 
						|
        if math.isnan(abs(self.expected)):  # type: ignore[arg-type]
 | 
						|
            return self.nan_ok and math.isnan(abs(actual))  # type: ignore[arg-type]
 | 
						|
 | 
						|
        # Infinity shouldn't be approximately equal to anything but itself, but
 | 
						|
        # if there's a relative tolerance, it will be infinite and infinity
 | 
						|
        # will seem approximately equal to everything.  The equal-to-itself
 | 
						|
        # case would have been short circuited above, so here we can just
 | 
						|
        # return false if the expected value is infinite.  The abs() call is
 | 
						|
        # for compatibility with complex numbers.
 | 
						|
        if math.isinf(abs(self.expected)):  # type: ignore[arg-type]
 | 
						|
            return False
 | 
						|
 | 
						|
        # Return true if the two numbers are within the tolerance.
 | 
						|
        result: bool = abs(self.expected - actual) <= self.tolerance
 | 
						|
        return result
 | 
						|
 | 
						|
    # Ignore type because of https://github.com/python/mypy/issues/4266.
 | 
						|
    __hash__ = None  # type: ignore
 | 
						|
 | 
						|
    @property
 | 
						|
    def tolerance(self):
 | 
						|
        """Return the tolerance for the comparison.
 | 
						|
 | 
						|
        This could be either an absolute tolerance or a relative tolerance,
 | 
						|
        depending on what the user specified or which would be larger.
 | 
						|
        """
 | 
						|
 | 
						|
        def set_default(x, default):
 | 
						|
            return x if x is not None else default
 | 
						|
 | 
						|
        # Figure out what the absolute tolerance should be.  ``self.abs`` is
 | 
						|
        # either None or a value specified by the user.
 | 
						|
        absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE)
 | 
						|
 | 
						|
        if absolute_tolerance < 0:
 | 
						|
            raise ValueError(
 | 
						|
                f"absolute tolerance can't be negative: {absolute_tolerance}"
 | 
						|
            )
 | 
						|
        if math.isnan(absolute_tolerance):
 | 
						|
            raise ValueError("absolute tolerance can't be NaN.")
 | 
						|
 | 
						|
        # If the user specified an absolute tolerance but not a relative one,
 | 
						|
        # just return the absolute tolerance.
 | 
						|
        if self.rel is None:
 | 
						|
            if self.abs is not None:
 | 
						|
                return absolute_tolerance
 | 
						|
 | 
						|
        # Figure out what the relative tolerance should be.  ``self.rel`` is
 | 
						|
        # either None or a value specified by the user.  This is done after
 | 
						|
        # we've made sure the user didn't ask for an absolute tolerance only,
 | 
						|
        # because we don't want to raise errors about the relative tolerance if
 | 
						|
        # we aren't even going to use it.
 | 
						|
        relative_tolerance = set_default(
 | 
						|
            self.rel, self.DEFAULT_RELATIVE_TOLERANCE
 | 
						|
        ) * abs(self.expected)
 | 
						|
 | 
						|
        if relative_tolerance < 0:
 | 
						|
            raise ValueError(
 | 
						|
                f"relative tolerance can't be negative: {relative_tolerance}"
 | 
						|
            )
 | 
						|
        if math.isnan(relative_tolerance):
 | 
						|
            raise ValueError("relative tolerance can't be NaN.")
 | 
						|
 | 
						|
        # Return the larger of the relative and absolute tolerances.
 | 
						|
        return max(relative_tolerance, absolute_tolerance)
 | 
						|
 | 
						|
 | 
						|
class ApproxDecimal(ApproxScalar):
 | 
						|
    """Perform approximate comparisons where the expected value is a Decimal."""
 | 
						|
 | 
						|
    DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12")
 | 
						|
    DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6")
 | 
						|
 | 
						|
 | 
						|
def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
 | 
						|
    """Assert that two numbers (or two ordered sequences of numbers) are equal to each other
 | 
						|
    within some tolerance.
 | 
						|
 | 
						|
    Due to the :doc:`python:tutorial/floatingpoint`, numbers that we
 | 
						|
    would intuitively expect to be equal are not always so::
 | 
						|
 | 
						|
        >>> 0.1 + 0.2 == 0.3
 | 
						|
        False
 | 
						|
 | 
						|
    This problem is commonly encountered when writing tests, e.g. when making
 | 
						|
    sure that floating-point values are what you expect them to be.  One way to
 | 
						|
    deal with this problem is to assert that two floating-point numbers are
 | 
						|
    equal to within some appropriate tolerance::
 | 
						|
 | 
						|
        >>> abs((0.1 + 0.2) - 0.3) < 1e-6
 | 
						|
        True
 | 
						|
 | 
						|
    However, comparisons like this are tedious to write and difficult to
 | 
						|
    understand.  Furthermore, absolute comparisons like the one above are
 | 
						|
    usually discouraged because there's no tolerance that works well for all
 | 
						|
    situations.  ``1e-6`` is good for numbers around ``1``, but too small for
 | 
						|
    very big numbers and too big for very small ones.  It's better to express
 | 
						|
    the tolerance as a fraction of the expected value, but relative comparisons
 | 
						|
    like that are even more difficult to write correctly and concisely.
 | 
						|
 | 
						|
    The ``approx`` class performs floating-point comparisons using a syntax
 | 
						|
    that's as intuitive as possible::
 | 
						|
 | 
						|
        >>> from pytest import approx
 | 
						|
        >>> 0.1 + 0.2 == approx(0.3)
 | 
						|
        True
 | 
						|
 | 
						|
    The same syntax also works for ordered sequences of numbers::
 | 
						|
 | 
						|
        >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6))
 | 
						|
        True
 | 
						|
 | 
						|
    ``numpy`` arrays::
 | 
						|
 | 
						|
        >>> import numpy as np                                                          # doctest: +SKIP
 | 
						|
        >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP
 | 
						|
        True
 | 
						|
 | 
						|
    And for a ``numpy`` array against a scalar::
 | 
						|
 | 
						|
        >>> import numpy as np                                         # doctest: +SKIP
 | 
						|
        >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP
 | 
						|
        True
 | 
						|
 | 
						|
    Only ordered sequences are supported, because ``approx`` needs
 | 
						|
    to infer the relative position of the sequences without ambiguity. This means
 | 
						|
    ``sets`` and other unordered sequences are not supported.
 | 
						|
 | 
						|
    Finally, dictionary *values* can also be compared::
 | 
						|
 | 
						|
        >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6})
 | 
						|
        True
 | 
						|
 | 
						|
    The comparison will be true if both mappings have the same keys and their
 | 
						|
    respective values match the expected tolerances.
 | 
						|
 | 
						|
    **Tolerances**
 | 
						|
 | 
						|
    By default, ``approx`` considers numbers within a relative tolerance of
 | 
						|
    ``1e-6`` (i.e. one part in a million) of its expected value to be equal.
 | 
						|
    This treatment would lead to surprising results if the expected value was
 | 
						|
    ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``.
 | 
						|
    To handle this case less surprisingly, ``approx`` also considers numbers
 | 
						|
    within an absolute tolerance of ``1e-12`` of its expected value to be
 | 
						|
    equal.  Infinity and NaN are special cases.  Infinity is only considered
 | 
						|
    equal to itself, regardless of the relative tolerance.  NaN is not
 | 
						|
    considered equal to anything by default, but you can make it be equal to
 | 
						|
    itself by setting the ``nan_ok`` argument to True.  (This is meant to
 | 
						|
    facilitate comparing arrays that use NaN to mean "no data".)
 | 
						|
 | 
						|
    Both the relative and absolute tolerances can be changed by passing
 | 
						|
    arguments to the ``approx`` constructor::
 | 
						|
 | 
						|
        >>> 1.0001 == approx(1)
 | 
						|
        False
 | 
						|
        >>> 1.0001 == approx(1, rel=1e-3)
 | 
						|
        True
 | 
						|
        >>> 1.0001 == approx(1, abs=1e-3)
 | 
						|
        True
 | 
						|
 | 
						|
    If you specify ``abs`` but not ``rel``, the comparison will not consider
 | 
						|
    the relative tolerance at all.  In other words, two numbers that are within
 | 
						|
    the default relative tolerance of ``1e-6`` will still be considered unequal
 | 
						|
    if they exceed the specified absolute tolerance.  If you specify both
 | 
						|
    ``abs`` and ``rel``, the numbers will be considered equal if either
 | 
						|
    tolerance is met::
 | 
						|
 | 
						|
        >>> 1 + 1e-8 == approx(1)
 | 
						|
        True
 | 
						|
        >>> 1 + 1e-8 == approx(1, abs=1e-12)
 | 
						|
        False
 | 
						|
        >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12)
 | 
						|
        True
 | 
						|
 | 
						|
    You can also use ``approx`` to compare nonnumeric types, or dicts and
 | 
						|
    sequences containing nonnumeric types, in which case it falls back to
 | 
						|
    strict equality. This can be useful for comparing dicts and sequences that
 | 
						|
    can contain optional values::
 | 
						|
 | 
						|
        >>> {"required": 1.0000005, "optional": None} == approx({"required": 1, "optional": None})
 | 
						|
        True
 | 
						|
        >>> [None, 1.0000005] == approx([None,1])
 | 
						|
        True
 | 
						|
        >>> ["foo", 1.0000005] == approx([None,1])
 | 
						|
        False
 | 
						|
 | 
						|
    If you're thinking about using ``approx``, then you might want to know how
 | 
						|
    it compares to other good ways of comparing floating-point numbers.  All of
 | 
						|
    these algorithms are based on relative and absolute tolerances and should
 | 
						|
    agree for the most part, but they do have meaningful differences:
 | 
						|
 | 
						|
    - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``:  True if the relative
 | 
						|
      tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute
 | 
						|
      tolerance is met.  Because the relative tolerance is calculated w.r.t.
 | 
						|
      both ``a`` and ``b``, this test is symmetric (i.e.  neither ``a`` nor
 | 
						|
      ``b`` is a "reference value").  You have to specify an absolute tolerance
 | 
						|
      if you want to compare to ``0.0`` because there is no tolerance by
 | 
						|
      default.  More information: :py:func:`math.isclose`.
 | 
						|
 | 
						|
    - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference
 | 
						|
      between ``a`` and ``b`` is less that the sum of the relative tolerance
 | 
						|
      w.r.t. ``b`` and the absolute tolerance.  Because the relative tolerance
 | 
						|
      is only calculated w.r.t. ``b``, this test is asymmetric and you can
 | 
						|
      think of ``b`` as the reference value.  Support for comparing sequences
 | 
						|
      is provided by :py:func:`numpy.allclose`.  More information:
 | 
						|
      :std:doc:`numpy:reference/generated/numpy.isclose`.
 | 
						|
 | 
						|
    - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b``
 | 
						|
      are within an absolute tolerance of ``1e-7``.  No relative tolerance is
 | 
						|
      considered , so this function is not appropriate for very large or very
 | 
						|
      small numbers.  Also, it's only available in subclasses of ``unittest.TestCase``
 | 
						|
      and it's ugly because it doesn't follow PEP8.  More information:
 | 
						|
      :py:meth:`unittest.TestCase.assertAlmostEqual`.
 | 
						|
 | 
						|
    - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative
 | 
						|
      tolerance is met w.r.t. ``b`` or if the absolute tolerance is met.
 | 
						|
      Because the relative tolerance is only calculated w.r.t. ``b``, this test
 | 
						|
      is asymmetric and you can think of ``b`` as the reference value.  In the
 | 
						|
      special case that you explicitly specify an absolute tolerance but not a
 | 
						|
      relative tolerance, only the absolute tolerance is considered.
 | 
						|
 | 
						|
    .. note::
 | 
						|
 | 
						|
        ``approx`` can handle numpy arrays, but we recommend the
 | 
						|
        specialised test helpers in :std:doc:`numpy:reference/routines.testing`
 | 
						|
        if you need support for comparisons, NaNs, or ULP-based tolerances.
 | 
						|
 | 
						|
        To match strings using regex, you can use
 | 
						|
        `Matches <https://github.com/asottile/re-assert#re_assertmatchespattern-str-args-kwargs>`_
 | 
						|
        from the
 | 
						|
        `re_assert package <https://github.com/asottile/re-assert>`_.
 | 
						|
 | 
						|
    .. warning::
 | 
						|
 | 
						|
       .. versionchanged:: 3.2
 | 
						|
 | 
						|
       In order to avoid inconsistent behavior, :py:exc:`TypeError` is
 | 
						|
       raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons.
 | 
						|
       The example below illustrates the problem::
 | 
						|
 | 
						|
           assert approx(0.1) > 0.1 + 1e-10  # calls approx(0.1).__gt__(0.1 + 1e-10)
 | 
						|
           assert 0.1 + 1e-10 > approx(0.1)  # calls approx(0.1).__lt__(0.1 + 1e-10)
 | 
						|
 | 
						|
       In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)``
 | 
						|
       to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to
 | 
						|
       comparison. This is because the call hierarchy of rich comparisons
 | 
						|
       follows a fixed behavior. More information: :py:meth:`object.__ge__`
 | 
						|
 | 
						|
    .. versionchanged:: 3.7.1
 | 
						|
       ``approx`` raises ``TypeError`` when it encounters a dict value or
 | 
						|
       sequence element of nonnumeric type.
 | 
						|
 | 
						|
    .. versionchanged:: 6.1.0
 | 
						|
       ``approx`` falls back to strict equality for nonnumeric types instead
 | 
						|
       of raising ``TypeError``.
 | 
						|
    """
 | 
						|
 | 
						|
    # Delegate the comparison to a class that knows how to deal with the type
 | 
						|
    # of the expected value (e.g. int, float, list, dict, numpy.array, etc).
 | 
						|
    #
 | 
						|
    # The primary responsibility of these classes is to implement ``__eq__()``
 | 
						|
    # and ``__repr__()``.  The former is used to actually check if some
 | 
						|
    # "actual" value is equivalent to the given expected value within the
 | 
						|
    # allowed tolerance.  The latter is used to show the user the expected
 | 
						|
    # value and tolerance, in the case that a test failed.
 | 
						|
    #
 | 
						|
    # 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):
 | 
						|
        cls: Type[ApproxBase] = ApproxDecimal
 | 
						|
    elif isinstance(expected, Mapping):
 | 
						|
        cls = ApproxMapping
 | 
						|
    elif _is_numpy_array(expected):
 | 
						|
        expected = _as_numpy_array(expected)
 | 
						|
        cls = ApproxNumpy
 | 
						|
    elif (
 | 
						|
        hasattr(expected, "__getitem__")
 | 
						|
        and isinstance(expected, Sized)
 | 
						|
        # Type ignored because the error is wrong -- not unreachable.
 | 
						|
        and not isinstance(expected, STRING_TYPES)  # type: ignore[unreachable]
 | 
						|
    ):
 | 
						|
        cls = ApproxSequenceLike
 | 
						|
    elif (
 | 
						|
        isinstance(expected, Collection)
 | 
						|
        # Type ignored because the error is wrong -- not unreachable.
 | 
						|
        and not isinstance(expected, STRING_TYPES)  # type: ignore[unreachable]
 | 
						|
    ):
 | 
						|
        msg = f"pytest.approx() only supports ordered sequences, but got: {repr(expected)}"
 | 
						|
        raise TypeError(msg)
 | 
						|
    else:
 | 
						|
        cls = ApproxScalar
 | 
						|
 | 
						|
    return cls(expected, rel, abs, nan_ok)
 | 
						|
 | 
						|
 | 
						|
def _is_numpy_array(obj: object) -> bool:
 | 
						|
    """
 | 
						|
    Return true if the given object is implicitly convertible to ndarray,
 | 
						|
    and numpy is already imported.
 | 
						|
    """
 | 
						|
    return _as_numpy_array(obj) is not None
 | 
						|
 | 
						|
 | 
						|
def _as_numpy_array(obj: object) -> Optional["ndarray"]:
 | 
						|
    """
 | 
						|
    Return an ndarray if the given object is implicitly convertible to ndarray,
 | 
						|
    and numpy is already imported, otherwise None.
 | 
						|
    """
 | 
						|
    import sys
 | 
						|
 | 
						|
    np: Any = sys.modules.get("numpy")
 | 
						|
    if np is not None:
 | 
						|
        # avoid infinite recursion on numpy scalars, which have __array__
 | 
						|
        if np.isscalar(obj):
 | 
						|
            return None
 | 
						|
        elif isinstance(obj, np.ndarray):
 | 
						|
            return obj
 | 
						|
        elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"):
 | 
						|
            return np.asarray(obj)
 | 
						|
    return None
 | 
						|
 | 
						|
 | 
						|
# builtin pytest.raises helper
 | 
						|
 | 
						|
E = TypeVar("E", bound=BaseException)
 | 
						|
 | 
						|
 | 
						|
@overload
 | 
						|
def raises(
 | 
						|
    expected_exception: Union[Type[E], Tuple[Type[E], ...]],
 | 
						|
    *,
 | 
						|
    match: Optional[Union[str, Pattern[str]]] = ...,
 | 
						|
) -> "RaisesContext[E]":
 | 
						|
    ...
 | 
						|
 | 
						|
 | 
						|
@overload
 | 
						|
def raises(  # noqa: F811
 | 
						|
    expected_exception: Union[Type[E], Tuple[Type[E], ...]],
 | 
						|
    func: Callable[..., Any],
 | 
						|
    *args: Any,
 | 
						|
    **kwargs: Any,
 | 
						|
) -> _pytest._code.ExceptionInfo[E]:
 | 
						|
    ...
 | 
						|
 | 
						|
 | 
						|
def raises(  # noqa: F811
 | 
						|
    expected_exception: Union[Type[E], Tuple[Type[E], ...]], *args: Any, **kwargs: Any
 | 
						|
) -> Union["RaisesContext[E]", _pytest._code.ExceptionInfo[E]]:
 | 
						|
    r"""Assert that a code block/function call raises an exception type, or one of its subclasses.
 | 
						|
 | 
						|
    :param expected_exception:
 | 
						|
        The expected exception type, or a tuple if one of multiple possible
 | 
						|
        exception types are expected. Note that subclasses of the passed exceptions
 | 
						|
        will also match.
 | 
						|
 | 
						|
    :kwparam str | re.Pattern[str] | None match:
 | 
						|
        If specified, a string containing a regular expression,
 | 
						|
        or a regular expression object, that is tested against the string
 | 
						|
        representation of the exception and its :pep:`678` `__notes__`
 | 
						|
        using :func:`re.search`.
 | 
						|
 | 
						|
        To match a literal string that may contain :ref:`special characters
 | 
						|
        <re-syntax>`, the pattern can first be escaped with :func:`re.escape`.
 | 
						|
 | 
						|
        (This is only used when ``pytest.raises`` is used as a context manager,
 | 
						|
        and passed through to the function otherwise.
 | 
						|
        When using ``pytest.raises`` as a function, you can use:
 | 
						|
        ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.)
 | 
						|
 | 
						|
    Use ``pytest.raises`` as a context manager, which will capture the exception of the given
 | 
						|
    type, or any of its subclasses::
 | 
						|
 | 
						|
        >>> import pytest
 | 
						|
        >>> with pytest.raises(ZeroDivisionError):
 | 
						|
        ...    1/0
 | 
						|
 | 
						|
    If the code block does not raise the expected exception (:class:`ZeroDivisionError` in the example
 | 
						|
    above), or no exception at all, the check will fail instead.
 | 
						|
 | 
						|
    You can also use the keyword argument ``match`` to assert that the
 | 
						|
    exception matches a text or regex::
 | 
						|
 | 
						|
        >>> with pytest.raises(ValueError, match='must be 0 or None'):
 | 
						|
        ...     raise ValueError("value must be 0 or None")
 | 
						|
 | 
						|
        >>> with pytest.raises(ValueError, match=r'must be \d+$'):
 | 
						|
        ...     raise ValueError("value must be 42")
 | 
						|
 | 
						|
    The ``match`` argument searches the formatted exception string, which includes any
 | 
						|
    `PEP-678 <https://peps.python.org/pep-0678/>`__ ``__notes__``:
 | 
						|
 | 
						|
        >>> with pytest.raises(ValueError, match=r'had a note added'):  # doctest: +SKIP
 | 
						|
        ...    e = ValueError("value must be 42")
 | 
						|
        ...    e.add_note("had a note added")
 | 
						|
        ...    raise e
 | 
						|
 | 
						|
    The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the
 | 
						|
    details of the captured exception::
 | 
						|
 | 
						|
        >>> with pytest.raises(ValueError) as exc_info:
 | 
						|
        ...     raise ValueError("value must be 42")
 | 
						|
        >>> assert exc_info.type is ValueError
 | 
						|
        >>> assert exc_info.value.args[0] == "value must be 42"
 | 
						|
 | 
						|
    .. warning::
 | 
						|
 | 
						|
       Given that ``pytest.raises`` matches subclasses, be wary of using it to match :class:`Exception` like this::
 | 
						|
 | 
						|
           with pytest.raises(Exception):  # Careful, this will catch ANY exception raised.
 | 
						|
                some_function()
 | 
						|
 | 
						|
       Because :class:`Exception` is the base class of almost all exceptions, it is easy for this to hide
 | 
						|
       real bugs, where the user wrote this expecting a specific exception, but some other exception is being
 | 
						|
       raised due to a bug introduced during a refactoring.
 | 
						|
 | 
						|
       Avoid using ``pytest.raises`` to catch :class:`Exception` unless certain that you really want to catch
 | 
						|
       **any** exception raised.
 | 
						|
 | 
						|
    .. note::
 | 
						|
 | 
						|
       When using ``pytest.raises`` as a context manager, it's worthwhile to
 | 
						|
       note that normal context manager rules apply and that the exception
 | 
						|
       raised *must* be the final line in the scope of the context manager.
 | 
						|
       Lines of code after that, within the scope of the context manager will
 | 
						|
       not be executed. For example::
 | 
						|
 | 
						|
           >>> value = 15
 | 
						|
           >>> with pytest.raises(ValueError) as exc_info:
 | 
						|
           ...     if value > 10:
 | 
						|
           ...         raise ValueError("value must be <= 10")
 | 
						|
           ...     assert exc_info.type is ValueError  # This will not execute.
 | 
						|
 | 
						|
       Instead, the following approach must be taken (note the difference in
 | 
						|
       scope)::
 | 
						|
 | 
						|
           >>> with pytest.raises(ValueError) as exc_info:
 | 
						|
           ...     if value > 10:
 | 
						|
           ...         raise ValueError("value must be <= 10")
 | 
						|
           ...
 | 
						|
           >>> assert exc_info.type is ValueError
 | 
						|
 | 
						|
    **Using with** ``pytest.mark.parametrize``
 | 
						|
 | 
						|
    When using :ref:`pytest.mark.parametrize ref`
 | 
						|
    it is possible to parametrize tests such that
 | 
						|
    some runs raise an exception and others do not.
 | 
						|
 | 
						|
    See :ref:`parametrizing_conditional_raising` for an example.
 | 
						|
 | 
						|
    .. seealso::
 | 
						|
 | 
						|
        :ref:`assertraises` for more examples and detailed discussion.
 | 
						|
 | 
						|
    **Legacy form**
 | 
						|
 | 
						|
    It is possible to specify a callable by passing a to-be-called lambda::
 | 
						|
 | 
						|
        >>> raises(ZeroDivisionError, lambda: 1/0)
 | 
						|
        <ExceptionInfo ...>
 | 
						|
 | 
						|
    or you can specify an arbitrary callable with arguments::
 | 
						|
 | 
						|
        >>> def f(x): return 1/x
 | 
						|
        ...
 | 
						|
        >>> raises(ZeroDivisionError, f, 0)
 | 
						|
        <ExceptionInfo ...>
 | 
						|
        >>> raises(ZeroDivisionError, f, x=0)
 | 
						|
        <ExceptionInfo ...>
 | 
						|
 | 
						|
    The form above is fully supported but discouraged for new code because the
 | 
						|
    context manager form is regarded as more readable and less error-prone.
 | 
						|
 | 
						|
    .. note::
 | 
						|
        Similar to caught exception objects in Python, explicitly clearing
 | 
						|
        local references to returned ``ExceptionInfo`` objects can
 | 
						|
        help the Python interpreter speed up its garbage collection.
 | 
						|
 | 
						|
        Clearing those references breaks a reference cycle
 | 
						|
        (``ExceptionInfo`` --> caught exception --> frame stack raising
 | 
						|
        the exception --> current frame stack --> local variables -->
 | 
						|
        ``ExceptionInfo``) which makes Python keep all objects referenced
 | 
						|
        from that cycle (including all local variables in the current
 | 
						|
        frame) alive until the next cyclic garbage collection run.
 | 
						|
        More detailed information can be found in the official Python
 | 
						|
        documentation for :ref:`the try statement <python:try>`.
 | 
						|
    """
 | 
						|
    __tracebackhide__ = True
 | 
						|
 | 
						|
    if not expected_exception:
 | 
						|
        raise ValueError(
 | 
						|
            f"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. "
 | 
						|
            f"Raising exceptions is already understood as failing the test, so you don't need "
 | 
						|
            f"any special code to say 'this should never raise an exception'."
 | 
						|
        )
 | 
						|
    if isinstance(expected_exception, type):
 | 
						|
        expected_exceptions: Tuple[Type[E], ...] = (expected_exception,)
 | 
						|
    else:
 | 
						|
        expected_exceptions = expected_exception
 | 
						|
    for exc in expected_exceptions:
 | 
						|
        if not isinstance(exc, type) or not issubclass(exc, BaseException):
 | 
						|
            msg = "expected exception must be a BaseException type, not {}"  # type: ignore[unreachable]
 | 
						|
            not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__
 | 
						|
            raise TypeError(msg.format(not_a))
 | 
						|
 | 
						|
    message = f"DID NOT RAISE {expected_exception}"
 | 
						|
 | 
						|
    if not args:
 | 
						|
        match: Optional[Union[str, Pattern[str]]] = kwargs.pop("match", None)
 | 
						|
        if kwargs:
 | 
						|
            msg = "Unexpected keyword arguments passed to pytest.raises: "
 | 
						|
            msg += ", ".join(sorted(kwargs))
 | 
						|
            msg += "\nUse context-manager form instead?"
 | 
						|
            raise TypeError(msg)
 | 
						|
        return RaisesContext(expected_exception, message, match)
 | 
						|
    else:
 | 
						|
        func = args[0]
 | 
						|
        if not callable(func):
 | 
						|
            raise TypeError(f"{func!r} object (type: {type(func)}) must be callable")
 | 
						|
        try:
 | 
						|
            func(*args[1:], **kwargs)
 | 
						|
        except expected_exception as e:
 | 
						|
            return _pytest._code.ExceptionInfo.from_exception(e)
 | 
						|
    fail(message)
 | 
						|
 | 
						|
 | 
						|
# This doesn't work with mypy for now. Use fail.Exception instead.
 | 
						|
raises.Exception = fail.Exception  # type: ignore
 | 
						|
 | 
						|
 | 
						|
@final
 | 
						|
class RaisesContext(ContextManager[_pytest._code.ExceptionInfo[E]]):
 | 
						|
    def __init__(
 | 
						|
        self,
 | 
						|
        expected_exception: Union[Type[E], Tuple[Type[E], ...]],
 | 
						|
        message: str,
 | 
						|
        match_expr: Optional[Union[str, Pattern[str]]] = None,
 | 
						|
    ) -> None:
 | 
						|
        self.expected_exception = expected_exception
 | 
						|
        self.message = message
 | 
						|
        self.match_expr = match_expr
 | 
						|
        self.excinfo: Optional[_pytest._code.ExceptionInfo[E]] = None
 | 
						|
 | 
						|
    def __enter__(self) -> _pytest._code.ExceptionInfo[E]:
 | 
						|
        self.excinfo = _pytest._code.ExceptionInfo.for_later()
 | 
						|
        return self.excinfo
 | 
						|
 | 
						|
    def __exit__(
 | 
						|
        self,
 | 
						|
        exc_type: Optional[Type[BaseException]],
 | 
						|
        exc_val: Optional[BaseException],
 | 
						|
        exc_tb: Optional[TracebackType],
 | 
						|
    ) -> bool:
 | 
						|
        __tracebackhide__ = True
 | 
						|
        if exc_type is None:
 | 
						|
            fail(self.message)
 | 
						|
        assert self.excinfo is not None
 | 
						|
        if not issubclass(exc_type, self.expected_exception):
 | 
						|
            return False
 | 
						|
        # Cast to narrow the exception type now that it's verified.
 | 
						|
        exc_info = cast(Tuple[Type[E], E, TracebackType], (exc_type, exc_val, exc_tb))
 | 
						|
        self.excinfo.fill_unfilled(exc_info)
 | 
						|
        if self.match_expr is not None:
 | 
						|
            self.excinfo.match(self.match_expr)
 | 
						|
        return True
 |