Skip to content

Commit a152cfe

Browse files
committed
approx raises TypeError in Python 2 for comparison operators other than != and ==
1 parent 57a232f commit a152cfe

File tree

4 files changed

+40
-16
lines changed

4 files changed

+40
-16
lines changed

_pytest/python_api.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@
77
from _pytest.runner 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

@@ -32,15 +45,12 @@ def __eq__(self, actual):
3245

3346
__hash__ = None
3447

35-
def __gt__(self, actual):
36-
raise NotImplementedError
37-
38-
def __lt__(self, actual):
39-
raise NotImplementedError
40-
4148
def __ne__(self, actual):
4249
return not (actual == self)
4350

51+
if sys.version_info.major == 2:
52+
__cmp__ = _cmp_raises_type_error
53+
4454
def _approx_scalar(self, x):
4555
return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok)
4656

@@ -66,11 +76,8 @@ def __repr__(self):
6676
return "approx({0!r})".format(list(
6777
self._approx_scalar(x) for x in self.expected))
6878

69-
def __gt__(self, actual):
70-
raise NotImplementedError
71-
72-
def __lt__(self, actual):
73-
raise NotImplementedError
79+
if sys.version_info.major == 2:
80+
__cmp__ = _cmp_raises_type_error
7481

7582
def __eq__(self, actual):
7683
import numpy as np
@@ -370,12 +377,14 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
370377
is asymmetric and you can think of ``b`` as the reference value. In the
371378
special case that you explicitly specify an absolute tolerance but not a
372379
relative tolerance, only the absolute tolerance is considered.
373-
380+
374381
.. warning::
375382
376-
In order to avoid inconsistent behavior, a ``NotImplementedError`` is
377-
raised for ``__lt__`` and ``__gt__`` comparisons. The example below
378-
illustrates the problem::
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::
379388
380389
assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10)
381390
assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10)

changelog/2003.feature

Lines changed: 0 additions & 1 deletion
This file was deleted.

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)