aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py2/_pytest/python_api.py
diff options
context:
space:
mode:
authordeshevoy <deshevoy@yandex-team.ru>2022-02-10 16:46:56 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:46:56 +0300
commite988f30484abe5fdeedcc7a5d3c226c01a21800c (patch)
tree0a217b173aabb57b7e51f8a169989b1a3e0309fe /contrib/python/pytest/py2/_pytest/python_api.py
parent33ee501c05d3f24036ae89766a858930ae66c548 (diff)
downloadydb-e988f30484abe5fdeedcc7a5d3c226c01a21800c.tar.gz
Restoring authorship annotation for <deshevoy@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/pytest/py2/_pytest/python_api.py')
-rw-r--r--contrib/python/pytest/py2/_pytest/python_api.py1334
1 files changed, 667 insertions, 667 deletions
diff --git a/contrib/python/pytest/py2/_pytest/python_api.py b/contrib/python/pytest/py2/_pytest/python_api.py
index f6e475c3a2..66253dad15 100644
--- a/contrib/python/pytest/py2/_pytest/python_api.py
+++ b/contrib/python/pytest/py2/_pytest/python_api.py
@@ -1,591 +1,591 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
-import math
-import pprint
-import sys
+import math
+import pprint
+import sys
import warnings
-from decimal import Decimal
-from numbers import Number
-
-from more_itertools.more import always_iterable
-from six.moves import filterfalse
-from six.moves import zip
-
-import _pytest._code
+from decimal import Decimal
+from numbers import Number
+
+from more_itertools.more import always_iterable
+from six.moves import filterfalse
+from six.moves import zip
+
+import _pytest._code
from _pytest import deprecated
-from _pytest.compat import isclass
+from _pytest.compat import isclass
from _pytest.compat import Iterable
-from _pytest.compat import Mapping
+from _pytest.compat import Mapping
from _pytest.compat import Sized
-from _pytest.compat import STRING_TYPES
-from _pytest.outcomes import fail
-
-BASE_TYPE = (type, STRING_TYPES)
-
-
-def _cmp_raises_type_error(self, other):
- """__cmp__ implementation which raises TypeError. Used
- by Approx base classes to implement only == and != and raise a
- TypeError for other comparisons.
-
- Needed in Python 2 only, Python 3 all it takes is not implementing the
- other operators at all.
- """
- __tracebackhide__ = True
- raise TypeError(
- "Comparison operators other than == and != not supported by approx objects"
- )
-
-
-def _non_numeric_type_error(value, at):
- at_str = " at {}".format(at) if at else ""
- return TypeError(
- "cannot make approximate comparisons to non-numeric values: {!r} {}".format(
- value, at_str
- )
- )
-
-
-# builtin pytest.approx helper
-
-
-class ApproxBase(object):
- """
- 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=False):
- __tracebackhide__ = True
- self.expected = expected
- self.abs = abs
- self.rel = rel
- self.nan_ok = nan_ok
- self._check_type()
-
- def __repr__(self):
- raise NotImplementedError
-
- def __eq__(self, actual):
- return all(
- a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual)
- )
-
- __hash__ = None
-
- def __ne__(self, actual):
- return not (actual == self)
-
- if sys.version_info[0] == 2:
- __cmp__ = _cmp_raises_type_error
-
- def _approx_scalar(self, x):
- 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):
- """
- 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.
- pass
-
-
-def _recursive_list_map(f, x):
- if isinstance(x, list):
- return list(_recursive_list_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):
- list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
- return "approx({!r})".format(list_scalars)
-
- if sys.version_info[0] == 2:
- __cmp__ = _cmp_raises_type_error
-
- def __eq__(self, actual):
- 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: # noqa
- raise TypeError("cannot compare '{}' to numpy.ndarray".format(actual))
-
- if not np.isscalar(actual) and actual.shape != self.expected.shape:
- return False
-
- return ApproxBase.__eq__(self, 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):
+from _pytest.compat import STRING_TYPES
+from _pytest.outcomes import fail
+
+BASE_TYPE = (type, STRING_TYPES)
+
+
+def _cmp_raises_type_error(self, other):
+ """__cmp__ implementation which raises TypeError. Used
+ by Approx base classes to implement only == and != and raise a
+ TypeError for other comparisons.
+
+ Needed in Python 2 only, Python 3 all it takes is not implementing the
+ other operators at all.
+ """
+ __tracebackhide__ = True
+ raise TypeError(
+ "Comparison operators other than == and != not supported by approx objects"
+ )
+
+
+def _non_numeric_type_error(value, at):
+ at_str = " at {}".format(at) if at else ""
+ return TypeError(
+ "cannot make approximate comparisons to non-numeric values: {!r} {}".format(
+ value, at_str
+ )
+ )
+
+
+# builtin pytest.approx helper
+
+
+class ApproxBase(object):
+ """
+ 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=False):
+ __tracebackhide__ = True
+ self.expected = expected
+ self.abs = abs
+ self.rel = rel
+ self.nan_ok = nan_ok
+ self._check_type()
+
+ def __repr__(self):
+ raise NotImplementedError
+
+ def __eq__(self, actual):
+ return all(
+ a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual)
+ )
+
+ __hash__ = None
+
+ def __ne__(self, actual):
+ return not (actual == self)
+
+ if sys.version_info[0] == 2:
+ __cmp__ = _cmp_raises_type_error
+
+ def _approx_scalar(self, x):
+ 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):
+ """
+ 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.
+ pass
+
+
+def _recursive_list_map(f, x):
+ if isinstance(x, list):
+ return list(_recursive_list_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):
+ list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
+ return "approx({!r})".format(list_scalars)
+
+ if sys.version_info[0] == 2:
+ __cmp__ = _cmp_raises_type_error
+
+ def __eq__(self, actual):
+ 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: # noqa
+ raise TypeError("cannot compare '{}' to numpy.ndarray".format(actual))
+
+ if not np.isscalar(actual) and actual.shape != self.expected.shape:
+ return False
+
+ return ApproxBase.__eq__(self, 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):
+ 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):
- return "approx({!r})".format(
- {k: self._approx_scalar(v) for k, v in self.expected.items()}
- )
-
- def __eq__(self, actual):
- if set(actual.keys()) != set(self.expected.keys()):
- return False
-
- return ApproxBase.__eq__(self, actual)
-
- def _yield_comparisons(self, actual):
- for k in self.expected.keys():
- yield actual[k], self.expected[k]
-
- def _check_type(self):
- __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)))
- elif not isinstance(value, Number):
- raise _non_numeric_type_error(self.expected, at="key={!r}".format(key))
-
-
+
+
+class ApproxMapping(ApproxBase):
+ """
+ Perform approximate comparisons where the expected value is a mapping with
+ numeric values (the keys can be anything).
+ """
+
+ def __repr__(self):
+ return "approx({!r})".format(
+ {k: self._approx_scalar(v) for k, v in self.expected.items()}
+ )
+
+ def __eq__(self, actual):
+ if set(actual.keys()) != set(self.expected.keys()):
+ return False
+
+ return ApproxBase.__eq__(self, actual)
+
+ def _yield_comparisons(self, actual):
+ for k in self.expected.keys():
+ yield actual[k], self.expected[k]
+
+ def _check_type(self):
+ __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)))
+ elif not isinstance(value, Number):
+ raise _non_numeric_type_error(self.expected, at="key={!r}".format(key))
+
+
class ApproxSequencelike(ApproxBase):
- """
- Perform approximate comparisons where the expected value is a sequence of
- numbers.
- """
-
- def __repr__(self):
- seq_type = type(self.expected)
- if seq_type not in (tuple, list, set):
- seq_type = list
- return "approx({!r})".format(
- seq_type(self._approx_scalar(x) for x in self.expected)
- )
-
- def __eq__(self, actual):
- if len(actual) != len(self.expected):
- return False
- return ApproxBase.__eq__(self, actual)
-
- def _yield_comparisons(self, actual):
- return zip(actual, self.expected)
-
- def _check_type(self):
- __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)))
- elif not isinstance(x, Number):
- raise _non_numeric_type_error(
- self.expected, at="index {}".format(index)
- )
-
-
-class ApproxScalar(ApproxBase):
- """
- Perform approximate comparisons where the expected value is a single number.
- """
-
- DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
- DEFAULT_RELATIVE_TOLERANCE = 1e-6
-
- def __repr__(self):
- """
- Return a string communicating both the expected value and the tolerance
- for the comparison being made, e.g. '1.0 +- 1e-6'. Use the unicode
- plus/minus symbol if this is python3 (it's too hard to get right for
- python2).
- """
- if isinstance(self.expected, complex):
- return str(self.expected)
-
- # Infinities aren't compared using tolerances, so don't show a
- # tolerance.
- if math.isinf(self.expected):
- 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 = "{:.1e}".format(self.tolerance)
- except ValueError:
- vetted_tolerance = "???"
-
- if sys.version_info[0] == 2:
- return "{} +- {}".format(self.expected, vetted_tolerance)
- else:
- return u"{} \u00b1 {}".format(self.expected, vetted_tolerance)
-
- def __eq__(self, actual):
- """
- Return true if the given value is equal to the expected value within
- the pre-specified tolerance.
- """
- if _is_numpy_array(actual):
- # Call ``__eq__()`` manually to prevent infinite-recursion with
- # numpy<1.13. See #3748.
- return all(self.__eq__(a) for a in actual.flat)
-
- # Short-circuit exact equality.
- if actual == self.expected:
- return True
-
- # 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)):
- return self.nan_ok and math.isnan(abs(actual))
-
- # 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)):
- return False
-
- # Return true if the two numbers are within the tolerance.
- return abs(self.expected - actual) <= self.tolerance
-
- __hash__ = None
-
- @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(
- "absolute tolerance can't be negative: {}".format(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(
- "relative tolerance can't be negative: {}".format(absolute_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=False):
- """
- Assert that two numbers (or two sets of numbers) are equal to each other
- within some tolerance.
-
- Due to the `intricacies of floating-point arithmetic`__, numbers that we
- would intuitively expect to be equal are not always so::
-
- >>> 0.1 + 0.2 == 0.3
- False
-
- __ https://docs.python.org/3/tutorial/floatingpoint.html
-
- 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 sequences of numbers::
-
- >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6))
- True
-
- Dictionary *values*::
-
- >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 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
-
- 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
-
- 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. Only available in python>=3.5. `More information...`__
-
- __ https://docs.python.org/3/library/math.html#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 ``numpy.allclose``. `More information...`__
-
- __ http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.isclose.html
-
- - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b``
- are within an absolute tolerance of ``1e-7``. No relative tolerance is
- considered and the absolute tolerance cannot be changed, 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...`__
-
+ """
+ Perform approximate comparisons where the expected value is a sequence of
+ numbers.
+ """
+
+ def __repr__(self):
+ seq_type = type(self.expected)
+ if seq_type not in (tuple, list, set):
+ seq_type = list
+ return "approx({!r})".format(
+ seq_type(self._approx_scalar(x) for x in self.expected)
+ )
+
+ def __eq__(self, actual):
+ if len(actual) != len(self.expected):
+ return False
+ return ApproxBase.__eq__(self, actual)
+
+ def _yield_comparisons(self, actual):
+ return zip(actual, self.expected)
+
+ def _check_type(self):
+ __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)))
+ elif not isinstance(x, Number):
+ raise _non_numeric_type_error(
+ self.expected, at="index {}".format(index)
+ )
+
+
+class ApproxScalar(ApproxBase):
+ """
+ Perform approximate comparisons where the expected value is a single number.
+ """
+
+ DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
+ DEFAULT_RELATIVE_TOLERANCE = 1e-6
+
+ def __repr__(self):
+ """
+ Return a string communicating both the expected value and the tolerance
+ for the comparison being made, e.g. '1.0 +- 1e-6'. Use the unicode
+ plus/minus symbol if this is python3 (it's too hard to get right for
+ python2).
+ """
+ if isinstance(self.expected, complex):
+ return str(self.expected)
+
+ # Infinities aren't compared using tolerances, so don't show a
+ # tolerance.
+ if math.isinf(self.expected):
+ 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 = "{:.1e}".format(self.tolerance)
+ except ValueError:
+ vetted_tolerance = "???"
+
+ if sys.version_info[0] == 2:
+ return "{} +- {}".format(self.expected, vetted_tolerance)
+ else:
+ return u"{} \u00b1 {}".format(self.expected, vetted_tolerance)
+
+ def __eq__(self, actual):
+ """
+ Return true if the given value is equal to the expected value within
+ the pre-specified tolerance.
+ """
+ if _is_numpy_array(actual):
+ # Call ``__eq__()`` manually to prevent infinite-recursion with
+ # numpy<1.13. See #3748.
+ return all(self.__eq__(a) for a in actual.flat)
+
+ # Short-circuit exact equality.
+ if actual == self.expected:
+ return True
+
+ # 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)):
+ return self.nan_ok and math.isnan(abs(actual))
+
+ # 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)):
+ return False
+
+ # Return true if the two numbers are within the tolerance.
+ return abs(self.expected - actual) <= self.tolerance
+
+ __hash__ = None
+
+ @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(
+ "absolute tolerance can't be negative: {}".format(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(
+ "relative tolerance can't be negative: {}".format(absolute_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=False):
+ """
+ Assert that two numbers (or two sets of numbers) are equal to each other
+ within some tolerance.
+
+ Due to the `intricacies of floating-point arithmetic`__, numbers that we
+ would intuitively expect to be equal are not always so::
+
+ >>> 0.1 + 0.2 == 0.3
+ False
+
+ __ https://docs.python.org/3/tutorial/floatingpoint.html
+
+ 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 sequences of numbers::
+
+ >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6))
+ True
+
+ Dictionary *values*::
+
+ >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 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
+
+ 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
+
+ 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. Only available in python>=3.5. `More information...`__
+
+ __ https://docs.python.org/3/library/math.html#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 ``numpy.allclose``. `More information...`__
+
+ __ http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.isclose.html
+
+ - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b``
+ are within an absolute tolerance of ``1e-7``. No relative tolerance is
+ considered and the absolute tolerance cannot be changed, 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...`__
+
__ https://docs.python.org/3/library/unittest.html#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.
-
- .. warning::
-
- .. versionchanged:: 3.2
-
- In order to avoid inconsistent behavior, ``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...`__
-
- __ https://docs.python.org/3/reference/datamodel.html#object.__ge__
- """
-
- # 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 = ApproxDecimal
- elif isinstance(expected, Number):
- cls = ApproxScalar
- elif isinstance(expected, Mapping):
- cls = ApproxMapping
- elif _is_numpy_array(expected):
- cls = ApproxNumpy
+
+ - ``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.
+
+ .. warning::
+
+ .. versionchanged:: 3.2
+
+ In order to avoid inconsistent behavior, ``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...`__
+
+ __ https://docs.python.org/3/reference/datamodel.html#object.__ge__
+ """
+
+ # 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 = ApproxDecimal
+ elif isinstance(expected, Number):
+ cls = ApproxScalar
+ elif isinstance(expected, Mapping):
+ cls = ApproxMapping
+ elif _is_numpy_array(expected):
+ cls = ApproxNumpy
elif (
isinstance(expected, Iterable)
and isinstance(expected, Sized)
and not isinstance(expected, STRING_TYPES)
):
cls = ApproxSequencelike
- else:
- raise _non_numeric_type_error(expected, at=None)
-
- return cls(expected, rel, abs, nan_ok)
-
-
-def _is_numpy_array(obj):
- """
- Return true if the given object is a numpy array. Make a special effort to
- avoid importing numpy unless it's really necessary.
- """
- import sys
-
- np = sys.modules.get("numpy")
- if np is not None:
- return isinstance(obj, np.ndarray)
- return False
-
-
-# builtin pytest.raises helper
-
-
-def raises(expected_exception, *args, **kwargs):
- r"""
- Assert that a code block/function call raises ``expected_exception``
+ else:
+ raise _non_numeric_type_error(expected, at=None)
+
+ return cls(expected, rel, abs, nan_ok)
+
+
+def _is_numpy_array(obj):
+ """
+ Return true if the given object is a numpy array. Make a special effort to
+ avoid importing numpy unless it's really necessary.
+ """
+ import sys
+
+ np = sys.modules.get("numpy")
+ if np is not None:
+ return isinstance(obj, np.ndarray)
+ return False
+
+
+# builtin pytest.raises helper
+
+
+def raises(expected_exception, *args, **kwargs):
+ r"""
+ Assert that a code block/function call raises ``expected_exception``
or raise a failure exception otherwise.
-
+
:kwparam match: if specified, a string containing a regular expression,
or a regular expression object, that is tested against the string
representation of the exception using ``re.search``. To match a literal
string that may contain `special characters`__, the pattern can
first be escaped with ``re.escape``.
-
+
__ https://docs.python.org/3/library/re.html#regular-expression-syntax
-
+
:kwparam message: **(deprecated since 4.1)** if specified, provides a custom failure message
if the exception is not raised. See :ref:`the deprecation docs <raises message deprecated>` for a workaround.
-
+
.. currentmodule:: _pytest._code
Use ``pytest.raises`` as a context manager, which will capture the exception of the given
type::
- >>> with raises(ZeroDivisionError):
- ... 1/0
-
+ >>> with raises(ZeroDivisionError):
+ ... 1/0
+
If the code block does not raise the expected exception (``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 raises(ValueError, match='must be 0 or None'):
... raise ValueError("value must be 0 or None")
-
+
>>> with raises(ValueError, match=r'must be \d+$'):
... raise ValueError("value must be 42")
@@ -605,139 +605,139 @@ def raises(expected_exception, *args, **kwargs):
is considered error prone as users often mean to use ``match`` instead.
See :ref:`the deprecation docs <raises message deprecated>` for a workaround.
- .. 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 raises(ValueError) as exc_info:
- ... if value > 10:
- ... raise ValueError("value must be <= 10")
+ .. 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 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 raises(ValueError) as exc_info:
- ... if value > 10:
- ... raise ValueError("value must be <= 10")
- ...
+
+ Instead, the following approach must be taken (note the difference in
+ scope)::
+
+ >>> with 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.
-
+
**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 ...>
-
+
+ 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. See the
- official Python ``try`` statement documentation for more detailed
- information.
-
- """
- __tracebackhide__ = True
- for exc in filterfalse(isclass, always_iterable(expected_exception, BASE_TYPE)):
- msg = (
- "exceptions must be old-style classes or"
- " derived from BaseException, not %s"
- )
- raise TypeError(msg % type(exc))
-
- message = "DID NOT RAISE {}".format(expected_exception)
- match_expr = None
-
- if not args:
- if "message" in kwargs:
- message = kwargs.pop("message")
+
+ .. 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. See the
+ official Python ``try`` statement documentation for more detailed
+ information.
+
+ """
+ __tracebackhide__ = True
+ for exc in filterfalse(isclass, always_iterable(expected_exception, BASE_TYPE)):
+ msg = (
+ "exceptions must be old-style classes or"
+ " derived from BaseException, not %s"
+ )
+ raise TypeError(msg % type(exc))
+
+ message = "DID NOT RAISE {}".format(expected_exception)
+ match_expr = None
+
+ if not args:
+ if "message" in kwargs:
+ message = kwargs.pop("message")
warnings.warn(deprecated.RAISES_MESSAGE_PARAMETER, stacklevel=2)
- if "match" in kwargs:
- match_expr = kwargs.pop("match")
- if kwargs:
- msg = "Unexpected keyword arguments passed to pytest.raises: "
+ if "match" in kwargs:
+ match_expr = kwargs.pop("match")
+ if kwargs:
+ msg = "Unexpected keyword arguments passed to pytest.raises: "
msg += ", ".join(sorted(kwargs))
- raise TypeError(msg)
- return RaisesContext(expected_exception, message, match_expr)
- elif isinstance(args[0], str):
+ raise TypeError(msg)
+ return RaisesContext(expected_exception, message, match_expr)
+ elif isinstance(args[0], str):
warnings.warn(deprecated.RAISES_EXEC, stacklevel=2)
(code,) = args
- assert isinstance(code, str)
- frame = sys._getframe(1)
- loc = frame.f_locals.copy()
- loc.update(kwargs)
- # print "raises frame scope: %r" % frame.f_locals
- try:
+ assert isinstance(code, str)
+ frame = sys._getframe(1)
+ loc = frame.f_locals.copy()
+ loc.update(kwargs)
+ # print "raises frame scope: %r" % frame.f_locals
+ try:
code = _pytest._code.Source(code).compile(_genframe=frame)
exec(code, frame.f_globals, loc)
- # XXX didn't mean f_globals == f_locals something special?
- # this is destroyed here ...
- except expected_exception:
+ # XXX didn't mean f_globals == f_locals something special?
+ # this is destroyed here ...
+ except expected_exception:
return _pytest._code.ExceptionInfo.from_current()
- else:
- func = args[0]
- try:
- func(*args[1:], **kwargs)
- except expected_exception:
+ else:
+ func = args[0]
+ try:
+ func(*args[1:], **kwargs)
+ except expected_exception:
return _pytest._code.ExceptionInfo.from_current()
- fail(message)
-
-
-raises.Exception = fail.Exception
-
-
-class RaisesContext(object):
- def __init__(self, expected_exception, message, match_expr):
- self.expected_exception = expected_exception
- self.message = message
- self.match_expr = match_expr
- self.excinfo = None
-
- def __enter__(self):
+ fail(message)
+
+
+raises.Exception = fail.Exception
+
+
+class RaisesContext(object):
+ def __init__(self, expected_exception, message, match_expr):
+ self.expected_exception = expected_exception
+ self.message = message
+ self.match_expr = match_expr
+ self.excinfo = None
+
+ def __enter__(self):
self.excinfo = _pytest._code.ExceptionInfo.for_later()
- return self.excinfo
-
- def __exit__(self, *tp):
- __tracebackhide__ = True
- if tp[0] is None:
- fail(self.message)
- self.excinfo.__init__(tp)
- suppress_exception = issubclass(self.excinfo.type, self.expected_exception)
- if sys.version_info[0] == 2 and suppress_exception:
- sys.exc_clear()
+ return self.excinfo
+
+ def __exit__(self, *tp):
+ __tracebackhide__ = True
+ if tp[0] is None:
+ fail(self.message)
+ self.excinfo.__init__(tp)
+ suppress_exception = issubclass(self.excinfo.type, self.expected_exception)
+ if sys.version_info[0] == 2 and suppress_exception:
+ sys.exc_clear()
if self.match_expr is not None and suppress_exception:
- self.excinfo.match(self.match_expr)
- return suppress_exception
+ self.excinfo.match(self.match_expr)
+ return suppress_exception