Skip to content

Commit 763c580

Browse files
authored
Merge pull request #2576 from maiksensi/feat/raise-not-implemented-for-lt-gt-in-approx
#2003 Change behavior of `approx.py` to only support `__eq__` comparison
2 parents e1aed8c + 80f4699 commit 763c580

File tree

4 files changed

+54
-0
lines changed

4 files changed

+54
-0
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ Lukas Bednar
101101
Luke Murphy
102102
Maciek Fijalkowski
103103
Maho
104+
Maik Figura
104105
Mandeep Bhutani
105106
Manuel Krebber
106107
Marc Schlaich

_pytest/python_api.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@
77
from _pytest.outcomes import fail
88
import _pytest._code
99

10+
11+
def _cmp_raises_type_error(self, other):
12+
"""__cmp__ implementation which raises TypeError. Used
13+
by Approx base classes to implement only == and != and raise a
14+
TypeError for other comparisons.
15+
16+
Needed in Python 2 only, Python 3 all it takes is not implementing the
17+
other operators at all.
18+
"""
19+
__tracebackhide__ = True
20+
raise TypeError('Comparison operators other than == and != not supported by approx objects')
21+
22+
1023
# builtin pytest.approx helper
1124

1225

@@ -35,6 +48,9 @@ def __eq__(self, actual):
3548
def __ne__(self, actual):
3649
return not (actual == self)
3750

51+
if sys.version_info[0] == 2:
52+
__cmp__ = _cmp_raises_type_error
53+
3854
def _approx_scalar(self, x):
3955
return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
4056

@@ -60,6 +76,9 @@ def __repr__(self):
6076
return "approx({0!r})".format(list(
6177
self._approx_scalar(x) for x in self.expected))
6278

79+
if sys.version_info[0] == 2:
80+
__cmp__ = _cmp_raises_type_error
81+
6382
def __eq__(self, actual):
6483
import numpy as np
6584

@@ -358,6 +377,24 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
358377
is asymmetric and you can think of ``b`` as the reference value. In the
359378
special case that you explicitly specify an absolute tolerance but not a
360379
relative tolerance, only the absolute tolerance is considered.
380+
381+
.. warning::
382+
383+
.. versionchanged:: 3.2
384+
385+
In order to avoid inconsistent behavior, ``TypeError`` is
386+
raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons.
387+
The example below illustrates the problem::
388+
389+
assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10)
390+
assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10)
391+
392+
In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)``
393+
to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to
394+
comparison. This is because the call hierarchy of rich comparisons
395+
follows a fixed behavior. `More information...`__
396+
397+
__ https://docs.python.org/3/reference/datamodel.html#object.__ge__
361398
"""
362399

363400
from collections import Mapping, Sequence

changelog/2003.removal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
``pytest.approx`` no longer supports ``>``, ``>=``, ``<`` and ``<=`` operators to avoid surprising/inconsistent
2+
behavior. See `the docs <https://docs.pytest.org/en/latest/builtin.html#pytest.approx>`_ for more information.

testing/python/approx.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# encoding: utf-8
2+
import operator
23
import sys
34
import pytest
45
import doctest
@@ -382,3 +383,16 @@ def test_foo():
382383
'*At index 0 diff: 3 != 4 * {0}'.format(expected),
383384
'=* 1 failed in *=',
384385
])
386+
387+
@pytest.mark.parametrize('op', [
388+
pytest.param(operator.le, id='<='),
389+
pytest.param(operator.lt, id='<'),
390+
pytest.param(operator.ge, id='>='),
391+
pytest.param(operator.gt, id='>'),
392+
])
393+
def test_comparison_operator_type_error(self, op):
394+
"""
395+
pytest.approx should raise TypeError for operators other than == and != (#2003).
396+
"""
397+
with pytest.raises(TypeError):
398+
op(1, approx(1, rel=1e-6, abs=1e-12))

0 commit comments

Comments
 (0)