Resolve merge conflict due to approx being moved.
This commit is contained in:
		
						commit
						8c22aee256
					
				|  | @ -6,7 +6,6 @@ import inspect | ||||||
| import sys | import sys | ||||||
| import os | import os | ||||||
| import collections | import collections | ||||||
| import math |  | ||||||
| from itertools import count | from itertools import count | ||||||
| 
 | 
 | ||||||
| import py | import py | ||||||
|  | @ -1091,587 +1090,6 @@ def _showfixtures_main(config, session): | ||||||
|                 red=True) |                 red=True) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # builtin pytest.raises helper |  | ||||||
| 
 |  | ||||||
| def raises(expected_exception, *args, **kwargs): |  | ||||||
|     """ |  | ||||||
|     Assert that a code block/function call raises ``expected_exception`` |  | ||||||
|     and raise a failure exception otherwise. |  | ||||||
| 
 |  | ||||||
|     This helper produces a ``ExceptionInfo()`` object (see below). |  | ||||||
| 
 |  | ||||||
|     If using Python 2.5 or above, you may use this function as a |  | ||||||
|     context manager:: |  | ||||||
| 
 |  | ||||||
|         >>> with raises(ZeroDivisionError): |  | ||||||
|         ...    1/0 |  | ||||||
| 
 |  | ||||||
|     .. versionchanged:: 2.10 |  | ||||||
| 
 |  | ||||||
|     In the context manager form you may use the keyword argument |  | ||||||
|     ``message`` to specify a custom failure message:: |  | ||||||
| 
 |  | ||||||
|         >>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"): |  | ||||||
|         ...    pass |  | ||||||
|         Traceback (most recent call last): |  | ||||||
|           ... |  | ||||||
|         Failed: Expecting ZeroDivisionError |  | ||||||
| 
 |  | ||||||
|     .. 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 == 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") |  | ||||||
|            ... |  | ||||||
|            >>> assert exc_info.type == ValueError |  | ||||||
| 
 |  | ||||||
|     Or you can 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") |  | ||||||
| 
 |  | ||||||
|     Or you can 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 ...> |  | ||||||
| 
 |  | ||||||
|     A third possibility is to use a string to be executed:: |  | ||||||
| 
 |  | ||||||
|         >>> raises(ZeroDivisionError, "f(0)") |  | ||||||
|         <ExceptionInfo ...> |  | ||||||
| 
 |  | ||||||
|     .. autoclass:: _pytest._code.ExceptionInfo |  | ||||||
|         :members: |  | ||||||
| 
 |  | ||||||
|     .. 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 |  | ||||||
|     msg = ("exceptions must be old-style classes or" |  | ||||||
|            " derived from BaseException, not %s") |  | ||||||
|     if isinstance(expected_exception, tuple): |  | ||||||
|         for exc in expected_exception: |  | ||||||
|             if not isclass(exc): |  | ||||||
|                 raise TypeError(msg % type(exc)) |  | ||||||
|     elif not isclass(expected_exception): |  | ||||||
|         raise TypeError(msg % type(expected_exception)) |  | ||||||
| 
 |  | ||||||
|     message = "DID NOT RAISE {0}".format(expected_exception) |  | ||||||
|     match_expr = None |  | ||||||
| 
 |  | ||||||
|     if not args: |  | ||||||
|         if "message" in kwargs: |  | ||||||
|             message = kwargs.pop("message") |  | ||||||
|         if "match" in kwargs: |  | ||||||
|             match_expr = kwargs.pop("match") |  | ||||||
|             message += " matching '{0}'".format(match_expr) |  | ||||||
|         return RaisesContext(expected_exception, message, match_expr) |  | ||||||
|     elif isinstance(args[0], str): |  | ||||||
|         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: |  | ||||||
|             code = _pytest._code.Source(code).compile() |  | ||||||
|             py.builtin.exec_(code, frame.f_globals, loc) |  | ||||||
|             # XXX didn'T mean f_globals == f_locals something special? |  | ||||||
|             #     this is destroyed here ... |  | ||||||
|         except expected_exception: |  | ||||||
|             return _pytest._code.ExceptionInfo() |  | ||||||
|     else: |  | ||||||
|         func = args[0] |  | ||||||
|         try: |  | ||||||
|             func(*args[1:], **kwargs) |  | ||||||
|         except expected_exception: |  | ||||||
|             return _pytest._code.ExceptionInfo() |  | ||||||
|     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 = object.__new__(_pytest._code.ExceptionInfo) |  | ||||||
|         return self.excinfo |  | ||||||
| 
 |  | ||||||
|     def __exit__(self, *tp): |  | ||||||
|         __tracebackhide__ = True |  | ||||||
|         if tp[0] is None: |  | ||||||
|             fail(self.message) |  | ||||||
|         if sys.version_info < (2, 7): |  | ||||||
|             # py26: on __exit__() exc_value often does not contain the |  | ||||||
|             # exception value. |  | ||||||
|             # http://bugs.python.org/issue7853 |  | ||||||
|             if not isinstance(tp[1], BaseException): |  | ||||||
|                 exc_type, value, traceback = tp |  | ||||||
|                 tp = exc_type, exc_type(value), traceback |  | ||||||
|         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: |  | ||||||
|             self.excinfo.match(self.match_expr) |  | ||||||
|         return suppress_exception |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # builtin pytest.approx helper |  | ||||||
| 
 |  | ||||||
| class ApproxBase(object): |  | ||||||
|     """ |  | ||||||
|     Provide shared utilities for making approximate comparisons between numbers  |  | ||||||
|     or sequences of numbers. |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def __init__(self, expected, rel=None, abs=None): |  | ||||||
|         self.expected = expected |  | ||||||
|         self.abs = abs |  | ||||||
|         self.rel = rel |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         return ', '.join( |  | ||||||
|                 repr(self._approx_scalar(x)) |  | ||||||
|                 for x in self._yield_expected()) |  | ||||||
| 
 |  | ||||||
|     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) |  | ||||||
| 
 |  | ||||||
|     def _approx_scalar(self, x): |  | ||||||
|         return ApproxScalar(x, rel=self.rel, abs=self.abs) |  | ||||||
| 
 |  | ||||||
|     def _yield_expected(self, actual): |  | ||||||
|         """ |  | ||||||
|         Yield all the expected values associated with this object.  This is  |  | ||||||
|         used to implement the `__repr__` method. |  | ||||||
|         """ |  | ||||||
|         raise NotImplementedError |  | ||||||
| 
 |  | ||||||
|     def _yield_comparisons(self, actual): |  | ||||||
|         """ |  | ||||||
|         Yield all the pairs of numbers to be compared.  This is used to  |  | ||||||
|         implement the `__eq__` method. |  | ||||||
|         """ |  | ||||||
|         raise NotImplementedError |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| try: |  | ||||||
|     import numpy as np |  | ||||||
| 
 |  | ||||||
|     class ApproxNumpy(ApproxBase, np.ndarray): |  | ||||||
|         """ |  | ||||||
|         Perform approximate comparisons for numpy arrays. |  | ||||||
| 
 |  | ||||||
|         This class must inherit from numpy.ndarray in order to allow the approx  |  | ||||||
|         to be on either side of the `==` operator.  The reason for this has to  |  | ||||||
|         do with how python decides whether to call `a.__eq__()` or `b.__eq__()`  |  | ||||||
|         when it encounters `a == b`.   |  | ||||||
|          |  | ||||||
|         If `a` and `b` are not related by inheritance, `a` gets priority.  So  |  | ||||||
|         as long as `a.__eq__` is defined, it will be called.  Because most  |  | ||||||
|         implementations of `a.__eq__` end up calling `b.__eq__`, this detail  |  | ||||||
|         usually doesn't matter.  However, `numpy.ndarray.__eq__` raises an  |  | ||||||
|         error complaining that "the truth value of an array with more than  |  | ||||||
|         one element is ambiguous. Use a.any() or a.all()" when compared with a  |  | ||||||
|         custom class, so `b.__eq__` never gets called. |  | ||||||
| 
 |  | ||||||
|         The trick is that the priority rules change if `a` and `b` are related  |  | ||||||
|         by inheritance.  Specifically, `b.__eq__` gets priority if `b` is a  |  | ||||||
|         subclass of `a`.  So we can guarantee that `ApproxNumpy.__eq__` gets  |  | ||||||
|         called by inheriting from `numpy.ndarray`. |  | ||||||
|         """ |  | ||||||
| 
 |  | ||||||
|         def __new__(cls, expected, rel=None, abs=None): |  | ||||||
|             """ |  | ||||||
|             Numpy uses __new__ (rather than __init__) to initialize objects.  |  | ||||||
|              |  | ||||||
|             The `expected` argument must be a numpy array.  This should be  |  | ||||||
|             ensured by the approx() delegator function. |  | ||||||
|             """ |  | ||||||
|             assert isinstance(expected, np.ndarray) |  | ||||||
|             obj = super(ApproxNumpy, cls).__new__(cls, expected.shape) |  | ||||||
|             obj.__init__(expected, rel, abs) |  | ||||||
|             return obj |  | ||||||
| 
 |  | ||||||
|         def __repr__(self): |  | ||||||
|             # It might be nice to rewrite this function to account for the  |  | ||||||
|             # shape of the array... |  | ||||||
|             return '[' + ApproxBase.__repr__(self) + ']' |  | ||||||
| 
 |  | ||||||
|         def __eq__(self, actual): |  | ||||||
|             try: |  | ||||||
|                 actual = np.array(actual) |  | ||||||
|             except: |  | ||||||
|                 raise ValueError("cannot cast '{0}' to numpy.ndarray".format(actual)) |  | ||||||
| 
 |  | ||||||
|             if actual.shape != self.expected.shape: |  | ||||||
|                 return False |  | ||||||
| 
 |  | ||||||
|             return ApproxBase.__eq__(self, actual) |  | ||||||
| 
 |  | ||||||
|         def _yield_expected(self): |  | ||||||
|             for x in self.expected: |  | ||||||
|                 yield x |  | ||||||
| 
 |  | ||||||
|         def _yield_comparisons(self, actual): |  | ||||||
|             # We can be sure that `actual` is a numpy array, because it's  |  | ||||||
|             # casted in `__eq__` before being passed to `ApproxBase.__eq__`,  |  | ||||||
|             # which is the only method that calls this one. |  | ||||||
|             for i in np.ndindex(self.expected.shape): |  | ||||||
|                 yield actual[i], self.expected[i] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| except ImportError: |  | ||||||
|     np = None |  | ||||||
| 
 |  | ||||||
| class ApproxMapping(ApproxBase): |  | ||||||
|     """ |  | ||||||
|     Perform approximate comparisons for mappings where the values are numbers  |  | ||||||
|     (the keys can be anything). |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         item = lambda k, v: "'{0}': {1}".format(k, self._approx_scalar(v)) |  | ||||||
|         return '{' + ', '.join(item(k,v) for k,v in self.expected.items()) + '}' |  | ||||||
| 
 |  | ||||||
|     def __eq__(self, actual): |  | ||||||
|         if actual.keys() != 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] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ApproxSequence(ApproxBase): |  | ||||||
|     """ |  | ||||||
|     Perform approximate comparisons for sequences of numbers. |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def __repr__(self): |  | ||||||
|         open, close = '()' if isinstance(self.expected, tuple) else '[]' |  | ||||||
|         return open + ApproxBase.__repr__(self) + close |  | ||||||
| 
 |  | ||||||
|     def __eq__(self, actual): |  | ||||||
|         if len(actual) != len(self.expected): |  | ||||||
|             return False |  | ||||||
|         return ApproxBase.__eq__(self, actual) |  | ||||||
|          |  | ||||||
|     def _yield_expected(self): |  | ||||||
|         return iter(self.expected) |  | ||||||
| 
 |  | ||||||
|     def _yield_comparisons(self, actual): |  | ||||||
|         return zip(actual, self.expected) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ApproxScalar(ApproxBase): |  | ||||||
|     """ |  | ||||||
|     Perform approximate comparisons for single numbers only. |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     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 '{0} +- {1}'.format(self.expected, vetted_tolerance) |  | ||||||
|         else: |  | ||||||
|             return u'{0} \u00b1 {1}'.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. |  | ||||||
|         """ |  | ||||||
|         from numbers import Number |  | ||||||
| 
 |  | ||||||
|         # Give a good error message we get values to compare that aren't  |  | ||||||
|         # numbers, rather than choking on them later on. |  | ||||||
|         if not isinstance(actual, Number): |  | ||||||
|             raise ValueError("approx can only compare numbers, not '{0}'".format(actual)) |  | ||||||
|         if not isinstance(self.expected, Number): |  | ||||||
|             raise ValueError("approx can only compare numbers, not '{0}'".format(self.expected)) |  | ||||||
| 
 |  | ||||||
|         # Short-circuit exact equality. |  | ||||||
|         if actual == self.expected: |  | ||||||
|             return True |  | ||||||
| 
 |  | ||||||
|         # 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. |  | ||||||
|         """ |  | ||||||
|         set_default = lambda x, default: 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, 1e-12) |  | ||||||
| 
 |  | ||||||
|         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, 1e-6) * 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) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def approx(expected, rel=None, abs=None): |  | ||||||
|     """ |  | ||||||
|     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 on sequences of numbers:: |  | ||||||
| 
 |  | ||||||
|         >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) |  | ||||||
|         True |  | ||||||
|         >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6}) |  | ||||||
|         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.  Infinite numbers are another special case.  They are only |  | ||||||
|     considered equal to themselves, regardless of the relative tolerance.  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. |  | ||||||
|     """ |  | ||||||
|      |  | ||||||
|     from collections import Mapping, Sequence |  | ||||||
|     try: |  | ||||||
|         String = basestring  # python2 |  | ||||||
|     except NameError: |  | ||||||
|         String = str, bytes  # python3 |  | ||||||
| 
 |  | ||||||
|     # 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).   |  | ||||||
|     # |  | ||||||
|     # This architecture is really driven by the need to support numpy arrays.   |  | ||||||
|     # The only way to override `==` for arrays without requiring that approx be  |  | ||||||
|     # the left operand is to inherit the approx object from `numpy.ndarray`.   |  | ||||||
|     # But that can't be a general solution, because it requires (1) numpy to be  |  | ||||||
|     # installed and (2) the expected value to be a numpy array.  So the general  |  | ||||||
|     # solution is to delegate each type of expected value to a different class. |  | ||||||
|     # |  | ||||||
|     # This has the advantage that it made it easy to support mapping types  |  | ||||||
|     # (i.e. dict).  The old code accepted mapping types, but would only compare  |  | ||||||
|     # their keys, which is probably not what most people would expect. |  | ||||||
| 
 |  | ||||||
|     if np and isinstance(expected, np.ndarray): |  | ||||||
|         cls = ApproxNumpy |  | ||||||
|     elif isinstance(expected, Mapping): |  | ||||||
|         cls = ApproxMapping |  | ||||||
|     elif isinstance(expected, Sequence) and not isinstance(expected, String): |  | ||||||
|         cls = ApproxSequence |  | ||||||
|     else: |  | ||||||
|         cls = ApproxScalar |  | ||||||
| 
 |  | ||||||
|     return cls(expected, rel, abs) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| # | # | ||||||
| #  the basic pytest Function item | #  the basic pytest Function item | ||||||
|  |  | ||||||
|  | @ -0,0 +1,588 @@ | ||||||
|  | import math | ||||||
|  | import sys | ||||||
|  | 
 | ||||||
|  | import py | ||||||
|  | 
 | ||||||
|  | from _pytest.compat import isclass | ||||||
|  | from _pytest.runner import fail | ||||||
|  | import _pytest._code | ||||||
|  | 
 | ||||||
|  | # builtin pytest.approx helper | ||||||
|  | 
 | ||||||
|  | class ApproxBase(object): | ||||||
|  |     """ | ||||||
|  |     Provide shared utilities for making approximate comparisons between numbers  | ||||||
|  |     or sequences of numbers. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     def __init__(self, expected, rel=None, abs=None): | ||||||
|  |         self.expected = expected | ||||||
|  |         self.abs = abs | ||||||
|  |         self.rel = rel | ||||||
|  | 
 | ||||||
|  |     def __repr__(self): | ||||||
|  |         return ', '.join( | ||||||
|  |                 repr(self._approx_scalar(x)) | ||||||
|  |                 for x in self._yield_expected()) | ||||||
|  | 
 | ||||||
|  |     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) | ||||||
|  | 
 | ||||||
|  |     def _approx_scalar(self, x): | ||||||
|  |         return ApproxScalar(x, rel=self.rel, abs=self.abs) | ||||||
|  | 
 | ||||||
|  |     def _yield_expected(self, actual): | ||||||
|  |         """ | ||||||
|  |         Yield all the expected values associated with this object.  This is  | ||||||
|  |         used to implement the `__repr__` method. | ||||||
|  |         """ | ||||||
|  |         raise NotImplementedError | ||||||
|  | 
 | ||||||
|  |     def _yield_comparisons(self, actual): | ||||||
|  |         """ | ||||||
|  |         Yield all the pairs of numbers to be compared.  This is used to  | ||||||
|  |         implement the `__eq__` method. | ||||||
|  |         """ | ||||||
|  |         raise NotImplementedError | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     import numpy as np | ||||||
|  | 
 | ||||||
|  |     class ApproxNumpy(ApproxBase, np.ndarray): | ||||||
|  |         """ | ||||||
|  |         Perform approximate comparisons for numpy arrays. | ||||||
|  | 
 | ||||||
|  |         This class must inherit from numpy.ndarray in order to allow the approx  | ||||||
|  |         to be on either side of the `==` operator.  The reason for this has to  | ||||||
|  |         do with how python decides whether to call `a.__eq__()` or `b.__eq__()`  | ||||||
|  |         when it encounters `a == b`.   | ||||||
|  |          | ||||||
|  |         If `a` and `b` are not related by inheritance, `a` gets priority.  So  | ||||||
|  |         as long as `a.__eq__` is defined, it will be called.  Because most  | ||||||
|  |         implementations of `a.__eq__` end up calling `b.__eq__`, this detail  | ||||||
|  |         usually doesn't matter.  However, `numpy.ndarray.__eq__` raises an  | ||||||
|  |         error complaining that "the truth value of an array with more than  | ||||||
|  |         one element is ambiguous. Use a.any() or a.all()" when compared with a  | ||||||
|  |         custom class, so `b.__eq__` never gets called. | ||||||
|  | 
 | ||||||
|  |         The trick is that the priority rules change if `a` and `b` are related  | ||||||
|  |         by inheritance.  Specifically, `b.__eq__` gets priority if `b` is a  | ||||||
|  |         subclass of `a`.  So we can guarantee that `ApproxNumpy.__eq__` gets  | ||||||
|  |         called by inheriting from `numpy.ndarray`. | ||||||
|  |         """ | ||||||
|  | 
 | ||||||
|  |         def __new__(cls, expected, rel=None, abs=None): | ||||||
|  |             """ | ||||||
|  |             Numpy uses __new__ (rather than __init__) to initialize objects.  | ||||||
|  |              | ||||||
|  |             The `expected` argument must be a numpy array.  This should be  | ||||||
|  |             ensured by the approx() delegator function. | ||||||
|  |             """ | ||||||
|  |             assert isinstance(expected, np.ndarray) | ||||||
|  |             obj = super(ApproxNumpy, cls).__new__(cls, expected.shape) | ||||||
|  |             obj.__init__(expected, rel, abs) | ||||||
|  |             return obj | ||||||
|  | 
 | ||||||
|  |         def __repr__(self): | ||||||
|  |             # It might be nice to rewrite this function to account for the  | ||||||
|  |             # shape of the array... | ||||||
|  |             return '[' + ApproxBase.__repr__(self) + ']' | ||||||
|  | 
 | ||||||
|  |         def __eq__(self, actual): | ||||||
|  |             try: | ||||||
|  |                 actual = np.array(actual) | ||||||
|  |             except: | ||||||
|  |                 raise ValueError("cannot cast '{0}' to numpy.ndarray".format(actual)) | ||||||
|  | 
 | ||||||
|  |             if actual.shape != self.expected.shape: | ||||||
|  |                 return False | ||||||
|  | 
 | ||||||
|  |             return ApproxBase.__eq__(self, actual) | ||||||
|  | 
 | ||||||
|  |         def _yield_expected(self): | ||||||
|  |             for x in self.expected: | ||||||
|  |                 yield x | ||||||
|  | 
 | ||||||
|  |         def _yield_comparisons(self, actual): | ||||||
|  |             # We can be sure that `actual` is a numpy array, because it's  | ||||||
|  |             # casted in `__eq__` before being passed to `ApproxBase.__eq__`,  | ||||||
|  |             # which is the only method that calls this one. | ||||||
|  |             for i in np.ndindex(self.expected.shape): | ||||||
|  |                 yield actual[i], self.expected[i] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | except ImportError: | ||||||
|  |     np = None | ||||||
|  | 
 | ||||||
|  | class ApproxMapping(ApproxBase): | ||||||
|  |     """ | ||||||
|  |     Perform approximate comparisons for mappings where the values are numbers  | ||||||
|  |     (the keys can be anything). | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     def __repr__(self): | ||||||
|  |         item = lambda k, v: "'{0}': {1}".format(k, self._approx_scalar(v)) | ||||||
|  |         return '{' + ', '.join(item(k,v) for k,v in self.expected.items()) + '}' | ||||||
|  | 
 | ||||||
|  |     def __eq__(self, actual): | ||||||
|  |         if actual.keys() != 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] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ApproxSequence(ApproxBase): | ||||||
|  |     """ | ||||||
|  |     Perform approximate comparisons for sequences of numbers. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     def __repr__(self): | ||||||
|  |         open, close = '()' if isinstance(self.expected, tuple) else '[]' | ||||||
|  |         return open + ApproxBase.__repr__(self) + close | ||||||
|  | 
 | ||||||
|  |     def __eq__(self, actual): | ||||||
|  |         if len(actual) != len(self.expected): | ||||||
|  |             return False | ||||||
|  |         return ApproxBase.__eq__(self, actual) | ||||||
|  |          | ||||||
|  |     def _yield_expected(self): | ||||||
|  |         return iter(self.expected) | ||||||
|  | 
 | ||||||
|  |     def _yield_comparisons(self, actual): | ||||||
|  |         return zip(actual, self.expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ApproxScalar(ApproxBase): | ||||||
|  |     """ | ||||||
|  |     Perform approximate comparisons for single numbers only. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     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 '{0} +- {1}'.format(self.expected, vetted_tolerance) | ||||||
|  |         else: | ||||||
|  |             return u'{0} \u00b1 {1}'.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. | ||||||
|  |         """ | ||||||
|  |         from numbers import Number | ||||||
|  | 
 | ||||||
|  |         # Give a good error message we get values to compare that aren't  | ||||||
|  |         # numbers, rather than choking on them later on. | ||||||
|  |         if not isinstance(actual, Number): | ||||||
|  |             raise ValueError("approx can only compare numbers, not '{0}'".format(actual)) | ||||||
|  |         if not isinstance(self.expected, Number): | ||||||
|  |             raise ValueError("approx can only compare numbers, not '{0}'".format(self.expected)) | ||||||
|  | 
 | ||||||
|  |         # Short-circuit exact equality. | ||||||
|  |         if actual == self.expected: | ||||||
|  |             return True | ||||||
|  | 
 | ||||||
|  |         # 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. | ||||||
|  |         """ | ||||||
|  |         set_default = lambda x, default: 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, 1e-12) | ||||||
|  | 
 | ||||||
|  |         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, 1e-6) * 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) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def approx(expected, rel=None, abs=None): | ||||||
|  |     """ | ||||||
|  |     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 on sequences of numbers:: | ||||||
|  | 
 | ||||||
|  |         >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) | ||||||
|  |         True | ||||||
|  |         >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6}) | ||||||
|  |         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.  Infinite numbers are another special case.  They are only | ||||||
|  |     considered equal to themselves, regardless of the relative tolerance.  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. | ||||||
|  |     """ | ||||||
|  |      | ||||||
|  |     from collections import Mapping, Sequence | ||||||
|  |     try: | ||||||
|  |         String = basestring  # python2 | ||||||
|  |     except NameError: | ||||||
|  |         String = str, bytes  # python3 | ||||||
|  | 
 | ||||||
|  |     # 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).   | ||||||
|  |     # | ||||||
|  |     # This architecture is really driven by the need to support numpy arrays.   | ||||||
|  |     # The only way to override `==` for arrays without requiring that approx be  | ||||||
|  |     # the left operand is to inherit the approx object from `numpy.ndarray`.   | ||||||
|  |     # But that can't be a general solution, because it requires (1) numpy to be  | ||||||
|  |     # installed and (2) the expected value to be a numpy array.  So the general  | ||||||
|  |     # solution is to delegate each type of expected value to a different class. | ||||||
|  |     # | ||||||
|  |     # This has the advantage that it made it easy to support mapping types  | ||||||
|  |     # (i.e. dict).  The old code accepted mapping types, but would only compare  | ||||||
|  |     # their keys, which is probably not what most people would expect. | ||||||
|  | 
 | ||||||
|  |     if np and isinstance(expected, np.ndarray): | ||||||
|  |         cls = ApproxNumpy | ||||||
|  |     elif isinstance(expected, Mapping): | ||||||
|  |         cls = ApproxMapping | ||||||
|  |     elif isinstance(expected, Sequence) and not isinstance(expected, String): | ||||||
|  |         cls = ApproxSequence | ||||||
|  |     else: | ||||||
|  |         cls = ApproxScalar | ||||||
|  | 
 | ||||||
|  |     return cls(expected, rel, abs) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # builtin pytest.raises helper | ||||||
|  | 
 | ||||||
|  | def raises(expected_exception, *args, **kwargs): | ||||||
|  |     """ | ||||||
|  |     Assert that a code block/function call raises ``expected_exception`` | ||||||
|  |     and raise a failure exception otherwise. | ||||||
|  | 
 | ||||||
|  |     This helper produces a ``ExceptionInfo()`` object (see below). | ||||||
|  | 
 | ||||||
|  |     If using Python 2.5 or above, you may use this function as a | ||||||
|  |     context manager:: | ||||||
|  | 
 | ||||||
|  |         >>> with raises(ZeroDivisionError): | ||||||
|  |         ...    1/0 | ||||||
|  | 
 | ||||||
|  |     .. versionchanged:: 2.10 | ||||||
|  | 
 | ||||||
|  |     In the context manager form you may use the keyword argument | ||||||
|  |     ``message`` to specify a custom failure message:: | ||||||
|  | 
 | ||||||
|  |         >>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"): | ||||||
|  |         ...    pass | ||||||
|  |         Traceback (most recent call last): | ||||||
|  |           ... | ||||||
|  |         Failed: Expecting ZeroDivisionError | ||||||
|  | 
 | ||||||
|  |     .. 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 == 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") | ||||||
|  |            ... | ||||||
|  |            >>> assert exc_info.type == ValueError | ||||||
|  | 
 | ||||||
|  |     Or you can 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") | ||||||
|  | 
 | ||||||
|  |     Or you can 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 ...> | ||||||
|  | 
 | ||||||
|  |     A third possibility is to use a string to be executed:: | ||||||
|  | 
 | ||||||
|  |         >>> raises(ZeroDivisionError, "f(0)") | ||||||
|  |         <ExceptionInfo ...> | ||||||
|  | 
 | ||||||
|  |     .. autoclass:: _pytest._code.ExceptionInfo | ||||||
|  |         :members: | ||||||
|  | 
 | ||||||
|  |     .. 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 | ||||||
|  |     msg = ("exceptions must be old-style classes or" | ||||||
|  |            " derived from BaseException, not %s") | ||||||
|  |     if isinstance(expected_exception, tuple): | ||||||
|  |         for exc in expected_exception: | ||||||
|  |             if not isclass(exc): | ||||||
|  |                 raise TypeError(msg % type(exc)) | ||||||
|  |     elif not isclass(expected_exception): | ||||||
|  |         raise TypeError(msg % type(expected_exception)) | ||||||
|  | 
 | ||||||
|  |     message = "DID NOT RAISE {0}".format(expected_exception) | ||||||
|  |     match_expr = None | ||||||
|  | 
 | ||||||
|  |     if not args: | ||||||
|  |         if "message" in kwargs: | ||||||
|  |             message = kwargs.pop("message") | ||||||
|  |         if "match" in kwargs: | ||||||
|  |             match_expr = kwargs.pop("match") | ||||||
|  |             message += " matching '{0}'".format(match_expr) | ||||||
|  |         return RaisesContext(expected_exception, message, match_expr) | ||||||
|  |     elif isinstance(args[0], str): | ||||||
|  |         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: | ||||||
|  |             code = _pytest._code.Source(code).compile() | ||||||
|  |             py.builtin.exec_(code, frame.f_globals, loc) | ||||||
|  |             # XXX didn'T mean f_globals == f_locals something special? | ||||||
|  |             #     this is destroyed here ... | ||||||
|  |         except expected_exception: | ||||||
|  |             return _pytest._code.ExceptionInfo() | ||||||
|  |     else: | ||||||
|  |         func = args[0] | ||||||
|  |         try: | ||||||
|  |             func(*args[1:], **kwargs) | ||||||
|  |         except expected_exception: | ||||||
|  |             return _pytest._code.ExceptionInfo() | ||||||
|  |     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 = object.__new__(_pytest._code.ExceptionInfo) | ||||||
|  |         return self.excinfo | ||||||
|  | 
 | ||||||
|  |     def __exit__(self, *tp): | ||||||
|  |         __tracebackhide__ = True | ||||||
|  |         if tp[0] is None: | ||||||
|  |             fail(self.message) | ||||||
|  |         if sys.version_info < (2, 7): | ||||||
|  |             # py26: on __exit__() exc_value often does not contain the | ||||||
|  |             # exception value. | ||||||
|  |             # http://bugs.python.org/issue7853 | ||||||
|  |             if not isinstance(tp[1], BaseException): | ||||||
|  |                 exc_type, value, traceback = tp | ||||||
|  |                 tp = exc_type, exc_type(value), traceback | ||||||
|  |         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: | ||||||
|  |             self.excinfo.match(self.match_expr) | ||||||
|  |         return suppress_exception | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Internal code move: move code for pytest.approx/pytest.raises to own files in order to cut down the size of python.py | ||||||
|  | @ -22,10 +22,11 @@ from _pytest.skipping import xfail | ||||||
| from _pytest.main import Item, Collector, File, Session | from _pytest.main import Item, Collector, File, Session | ||||||
| from _pytest.fixtures import fillfixtures as _fillfuncargs | from _pytest.fixtures import fillfixtures as _fillfuncargs | ||||||
| from _pytest.python import ( | from _pytest.python import ( | ||||||
|     raises, approx, |  | ||||||
|     Module, Class, Instance, Function, Generator, |     Module, Class, Instance, Function, Generator, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | from _pytest.python_api import approx, raises | ||||||
|  | 
 | ||||||
| set_trace = __pytestPDB.set_trace | set_trace = __pytestPDB.set_trace | ||||||
| 
 | 
 | ||||||
| __all__ = [ | __all__ = [ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue