pytest: `pytest.approx` fails with `TypeError` when testing approximate value of a `decimal.Decimal`

description

When testing the approximate value of a decimal.Decimal the following TypeError occurrs TypeError: unsupported operand type(s) for -: 'float' and 'decimal.Decimal'

I would expect pytest.approx to test for approximate equality with a floating point value without having to either convert the decimal to a float or testing explicitly against another decimal.

pip list

Package      Version
------------ -------
atomicwrites 1.4.0
attrs        20.3.0
colorama     0.4.4
iniconfig    1.1.1
packaging    20.9
pip          21.0.1
pluggy       0.13.1
py           1.10.0
pyparsing    2.4.7
pytest       6.2.2
setuptools   54.2.0
toml         0.10.2

pytest and os versions

pytest: 6.2.2 os: windows 10

minimal example

import decimal
import pytest
import math

3.14159 == pytest.approx(math.pi)  # works
decimal.Decimal('3.14159') == pytest.approx(math.pi)  # raises TypeError

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Comments: 26 (19 by maintainers)

Most upvoted comments

in my opinion “sometimes working” is worse as it gives users a false sense of correctness (and can hide bugs)

this isn’t the case, approx() adjusts the value to create a range and then performs a comparison. moving a value by an epsilon is a key component of approx

Yes, the tolerance is added to/subtracted from the expected value. So I agree that you shouldn’t be able to use a Decimal tolerance with a float expected value, or vice versa. But only comparisons are made between the actual value and the tolerance-adjusted expected value, so there’s no reason why the example given in this issue shouldn’t work:

>>> decimal.Decimal('3.14159') == pytest.approx(math.pi)

This is more-or-less equivalent to:

>>> math.pi - 1e-5 < Decimal('3.14159') < math.pi + 1e-5
True

It’s worth mentioning again that Decimal explicitly allows comparisons with float:

>>> from decimal import Decimal
>>> Decimal(1) > 0.5
True
>>> Decimal(1) + 0.5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'decimal.Decimal' and 'float'

Since approx() only uses comparisons, it does make sense that this operation should succeed.

@asottile i primarily think we ought to support it in some way as we loosened up the strictness of approx to be a approachable general tools,

we should definitively warn when we approx compare floats vs Decimals, but we shouldn’t just plainly error out