Skip to content

Commit

Permalink
Use a simple +- ASCII string in the string representation of pyte…
Browse files Browse the repository at this point in the history
…st.approx In Python 2

Fix pytest-dev#2111
  • Loading branch information
nicoddemus committed Dec 2, 2016
1 parent 5365f7c commit a5dd73f
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 22 deletions.
21 changes: 14 additions & 7 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
* Provide ``:ref:`` targets for ``recwarn.rst`` so we can use intersphinx referencing.
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
``pytest.Function``, ``pytest.Module``, etc., instead (`#2034`_).
Thanks `@nmundar`_ for the PR.
Expand Down Expand Up @@ -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
.. _@d-b-w: https://bitbucket.org/d-b-w/
.. _@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
.. _@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
.. _#2034: https://github.com/pytest-dev/pytest/issues/2034
.. _#2038: https://github.com/pytest-dev/pytest/issues/2038
.. _#2078: https://github.com/pytest-dev/pytest/issues/2078
.. _#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
.. _#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
Expand Down
10 changes: 2 additions & 8 deletions _pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -1434,16 +1434,10 @@ def __repr__(self):
except ValueError:
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:
return plus_minus.encode('utf-8')
return '{0} +- {1}'.format(self.expected, vetted_tolerance)
else:
return plus_minus
return u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)

def __eq__(self, actual):
# Short-circuit exact equality.
Expand Down
36 changes: 29 additions & 7 deletions testing/python/approx.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# encoding: utf-8

import sys
import pytest
import doctest

Expand All @@ -9,6 +9,7 @@
from fractions import Fraction
inf, nan = float('inf'), float('nan')


class MyDocTestRunner(doctest.DocTestRunner):

def __init__(self):
Expand All @@ -23,12 +24,13 @@ class TestApprox:

def test_repr_string(self):
# Just make sure the Unicode handling doesn't raise any exceptions.
print(approx(1.0))
print(approx([1.0, 2.0, 3.0]))
print(approx(inf))
print(approx(1.0, rel=nan))
print(approx(1.0, rel=inf))
print(approx(1.0j, rel=inf))
plus_minus = u'\u00b1' if sys.version_info[0] > 2 else u'+-'
assert repr(approx(1.0)) == '1.0 {pm} 1.0e-06'.format(pm=plus_minus)
assert repr(approx([1.0, 2.0])) == '1.0 {pm} 1.0e-06, 2.0 {pm} 2.0e-06'.format(pm=plus_minus)
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} inf'.format(pm=plus_minus)
assert repr(approx(1.0j, rel=inf)) == '1j'

def test_operator_overloading(self):
assert 1 == approx(1, rel=1e-6, abs=1e-12)
Expand Down Expand Up @@ -285,3 +287,23 @@ def test_doctests(self):
runner = MyDocTestRunner()
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 *=',
])

0 comments on commit a5dd73f

Please sign in to comment.