Use a simple ``+-`` ASCII string in the string representation of pytest.approx In Python 2

Fix #2111
This commit is contained in:
Bruno Oliveira 2016-12-02 19:28:45 -02:00
parent 5365f7c9ca
commit 57c4489916
3 changed files with 49 additions and 23 deletions

View File

@ -15,6 +15,11 @@
* Provide ``:ref:`` targets for ``recwarn.rst`` so we can use intersphinx referencing. * Provide ``:ref:`` targets for ``recwarn.rst`` so we can use intersphinx referencing.
Thanks to `@dupuy`_ for the report and `@lwm`_ for the PR. Thanks to `@dupuy`_ for the report and `@lwm`_ for the PR.
* In Python 2, use a simple ``+-`` ASCII string in the string representation of ``pytest.approx`` (for example ``"4 +- 4.0e-06"``)
because it is brittle to handle that in different contexts and representations internally in pytest
which can result in bugs such as `#2111`_. In Python 3, the representation still uses ``±`` (for example ``4 ± 4.0e-06``).
Thanks `@kerrick-lyft`_ for the report and `@nicoddemus`_ for the PR.
* Using ``item.Function``, ``item.Module``, etc., is now issuing deprecation warnings, prefer * Using ``item.Function``, ``item.Module``, etc., is now issuing deprecation warnings, prefer
``pytest.Function``, ``pytest.Module``, etc., instead (`#2034`_). ``pytest.Function``, ``pytest.Module``, etc., instead (`#2034`_).
Thanks `@nmundar`_ for the PR. Thanks `@nmundar`_ for the PR.
@ -42,25 +47,27 @@
* *
.. _@mbukatov: https://github.com/mbukatov
.. _@dupuy: https://bitbucket.org/dupuy/
.. _@d-b-w: https://bitbucket.org/d-b-w/
.. _@lwm: https://github.com/lwm
.. _@adler-j: https://github.com/adler-j .. _@adler-j: https://github.com/adler-j
.. _@d-b-w: https://bitbucket.org/d-b-w/
.. _@DuncanBetts: https://github.com/DuncanBetts .. _@DuncanBetts: https://github.com/DuncanBetts
.. _@dupuy: https://bitbucket.org/dupuy/
.. _@kerrick-lyft: https://github.com/kerrick-lyft
.. _@lwm: https://github.com/lwm
.. _@mbukatov: https://github.com/mbukatov
.. _@nedbat: https://github.com/nedbat .. _@nedbat: https://github.com/nedbat
.. _@nmundar: https://github.com/nmundar .. _@nmundar: https://github.com/nmundar
.. _#2089: https://github.com/pytest-dev/pytest/issues/2089
.. _#478: https://github.com/pytest-dev/pytest/issues/478
.. _#687: https://github.com/pytest-dev/pytest/issues/687
.. _#2016: https://github.com/pytest-dev/pytest/issues/2016 .. _#2016: https://github.com/pytest-dev/pytest/issues/2016
.. _#2034: https://github.com/pytest-dev/pytest/issues/2034 .. _#2034: https://github.com/pytest-dev/pytest/issues/2034
.. _#2038: https://github.com/pytest-dev/pytest/issues/2038 .. _#2038: https://github.com/pytest-dev/pytest/issues/2038
.. _#2078: https://github.com/pytest-dev/pytest/issues/2078 .. _#2078: https://github.com/pytest-dev/pytest/issues/2078
.. _#2082: https://github.com/pytest-dev/pytest/issues/2082 .. _#2082: https://github.com/pytest-dev/pytest/issues/2082
.. _#2089: https://github.com/pytest-dev/pytest/issues/2089
.. _#2103: https://github.com/pytest-dev/pytest/issues/2103 .. _#2103: https://github.com/pytest-dev/pytest/issues/2103
.. _#2105: https://github.com/pytest-dev/pytest/issues/2105 .. _#2105: https://github.com/pytest-dev/pytest/issues/2105
.. _#2111: https://github.com/pytest-dev/pytest/issues/2111
.. _#478: https://github.com/pytest-dev/pytest/issues/478
.. _#687: https://github.com/pytest-dev/pytest/issues/687
3.0.4 3.0.4

View File

@ -1434,16 +1434,10 @@ class ApproxNonIterable(object):
except ValueError: except ValueError:
vetted_tolerance = '???' vetted_tolerance = '???'
plus_minus = u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
# In python2, __repr__() must return a string (i.e. not a unicode
# object). In python3, __repr__() must return a unicode object
# (although now strings are unicode objects and bytes are what
# strings were).
if sys.version_info[0] == 2: if sys.version_info[0] == 2:
return plus_minus.encode('utf-8') return '{0} +- {1}'.format(self.expected, vetted_tolerance)
else: else:
return plus_minus return u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
def __eq__(self, actual): def __eq__(self, actual):
# Short-circuit exact equality. # Short-circuit exact equality.

View File

@ -1,5 +1,5 @@
# encoding: utf-8 # encoding: utf-8
import sys
import pytest import pytest
import doctest import doctest
@ -9,6 +9,7 @@ from decimal import Decimal
from fractions import Fraction from fractions import Fraction
inf, nan = float('inf'), float('nan') inf, nan = float('inf'), float('nan')
class MyDocTestRunner(doctest.DocTestRunner): class MyDocTestRunner(doctest.DocTestRunner):
def __init__(self): def __init__(self):
@ -22,13 +23,17 @@ class MyDocTestRunner(doctest.DocTestRunner):
class TestApprox: class TestApprox:
def test_repr_string(self): def test_repr_string(self):
# Just make sure the Unicode handling doesn't raise any exceptions. # for some reason in Python 2.6 it is not displaying the tolerance representation correctly
print(approx(1.0)) plus_minus = u'\u00b1' if sys.version_info[0] > 2 else u'+-'
print(approx([1.0, 2.0, 3.0])) tol1, tol2, infr = '1.0e-06', '2.0e-06', 'inf'
print(approx(inf)) if sys.version_info[:2] == (2, 6):
print(approx(1.0, rel=nan)) tol1, tol2, infr = '???', '???', '???'
print(approx(1.0, rel=inf)) assert repr(approx(1.0)) == '1.0 {pm} {tol1}'.format(pm=plus_minus, tol1=tol1)
print(approx(1.0j, rel=inf)) assert repr(approx([1.0, 2.0])) == '1.0 {pm} {tol1}, 2.0 {pm} {tol2}'.format(pm=plus_minus, tol1=tol1, tol2=tol2)
assert repr(approx(inf)) == 'inf'
assert repr(approx(1.0, rel=nan)) == '1.0 {pm} ???'.format(pm=plus_minus)
assert repr(approx(1.0, rel=inf)) == '1.0 {pm} {infr}'.format(pm=plus_minus, infr=infr)
assert repr(approx(1.0j, rel=inf)) == '1j'
def test_operator_overloading(self): def test_operator_overloading(self):
assert 1 == approx(1, rel=1e-6, abs=1e-12) assert 1 == approx(1, rel=1e-6, abs=1e-12)
@ -285,3 +290,23 @@ class TestApprox:
runner = MyDocTestRunner() runner = MyDocTestRunner()
runner.run(test) runner.run(test)
def test_unicode_plus_minus(self, testdir):
"""
Comparing approx instances inside lists should not produce an error in the detailed diff.
Integration test for issue #2111.
"""
testdir.makepyfile("""
import pytest
def test_foo():
assert [3] == [pytest.approx(4)]
""")
expected = '4.0e-06'
# for some reason in Python 2.6 it is not displaying the tolerance representation correctly
if sys.version_info[:2] == (2, 6):
expected = '???'
result = testdir.runpytest()
result.stdout.fnmatch_lines([
'*At index 0 diff: 3 != 4 * {0}'.format(expected),
'=* 1 failed in *=',
])