From 7bff5866b1dd7992d9bdcdce1bac41fe043760c1 Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 18 Jul 2018 17:29:55 -0400 Subject: [PATCH 01/10] bugfix in ApproxNumpy initialisation, use keywords for arguments now --- src/_pytest/python_api.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index d88d1c88b..f35351e2a 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -211,7 +211,10 @@ class ApproxScalar(ApproxBase): the pre-specified tolerance. """ if _is_numpy_array(actual): - return ApproxNumpy(actual, self.abs, self.rel, self.nan_ok) == self.expected + return ( + ApproxNumpy(actual, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) + == self.expected + ) # Short-circuit exact equality. if actual == self.expected: From 75db6084792c3b33c56b57c1a5c5e7ea612f84da Mon Sep 17 00:00:00 2001 From: Alan Brammer Date: Wed, 18 Jul 2018 17:56:00 -0400 Subject: [PATCH 02/10] update changelog --- changelog/3695.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3695.bugfix.rst diff --git a/changelog/3695.bugfix.rst b/changelog/3695.bugfix.rst new file mode 100644 index 000000000..e9e258fd4 --- /dev/null +++ b/changelog/3695.bugfix.rst @@ -0,0 +1 @@ +Fix ApproxNumpy initialisation argument mixup. abs rel tolerances were previously flipped From 514ca6f4adcfe24efd0f221d7d00220275446f76 Mon Sep 17 00:00:00 2001 From: abrammer Date: Mon, 23 Jul 2018 23:40:06 -0400 Subject: [PATCH 03/10] add test wrt #3695 checking numpy array tolerance args --- testing/python/approx.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/testing/python/approx.py b/testing/python/approx.py index 39f10a821..b93ff241b 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -342,6 +342,20 @@ class TestApprox(object): assert actual == approx(list(expected), rel=5e-7, abs=0) assert actual != approx(list(expected), rel=5e-8, abs=0) + def test_numpy_tolerance_args(self): + """ + quick check that numpy rel/abs args are handled correctly + for comparison against an np.array + """ + np = pytest.importorskip("numpy") + expected = 100 + actual = 99 + assert actual != pytest.approx(expected, abs=0.1, rel=0) + assert np.array(actual) != pytest.approx(expected, abs=0.1, rel=0) + + assert actual == pytest.approx(expected, abs=0, rel=0.01) + assert np.array(actual) == pytest.approx(expected, abs=0, rel=0.1) + def test_numpy_array_wrong_shape(self): np = pytest.importorskip("numpy") From f0db64ac2e1ba54281c62049f191f1bb2c3565e7 Mon Sep 17 00:00:00 2001 From: abrammer Date: Tue, 24 Jul 2018 21:18:44 -0400 Subject: [PATCH 04/10] drop the duplicate approx call update test to include both np.array(actual) and np.array(expected) --- src/_pytest/python_api.py | 7 +++---- testing/python/approx.py | 9 ++++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index f35351e2a..04022b9fe 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -211,10 +211,9 @@ class ApproxScalar(ApproxBase): the pre-specified tolerance. """ if _is_numpy_array(actual): - return ( - ApproxNumpy(actual, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) - == self.expected - ) + import numpy as np + + return np.all(abs(self.expected - actual) <= self.tolerance) # Short-circuit exact equality. if actual == self.expected: diff --git a/testing/python/approx.py b/testing/python/approx.py index b93ff241b..09487cc73 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -346,15 +346,22 @@ class TestApprox(object): """ quick check that numpy rel/abs args are handled correctly for comparison against an np.array + - 3.6.4 would approx both actual / expected if np.array + regardless of which value was passed to approx() + Means tolerance could be calculated against bad test return """ np = pytest.importorskip("numpy") expected = 100 actual = 99 assert actual != pytest.approx(expected, abs=0.1, rel=0) assert np.array(actual) != pytest.approx(expected, abs=0.1, rel=0) + assert actual != pytest.approx(np.array(expected), abs=0.1, rel=0) + assert np.array(actual) != pytest.approx(np.array(expected), abs=0.1, rel=0) assert actual == pytest.approx(expected, abs=0, rel=0.01) - assert np.array(actual) == pytest.approx(expected, abs=0, rel=0.1) + assert np.array(actual) == pytest.approx(expected, abs=0, rel=0.01) + assert actual == pytest.approx(np.array(expected), abs=0, rel=0.01) + assert np.array(actual) == pytest.approx(np.array(expected), abs=0, rel=0.01) def test_numpy_array_wrong_shape(self): np = pytest.importorskip("numpy") From 2eb9301ad553961bd4a4a48519df867879a03f35 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 25 Jul 2018 08:09:31 -0300 Subject: [PATCH 05/10] Improve CHANGELOG --- changelog/3695.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/3695.bugfix.rst b/changelog/3695.bugfix.rst index e9e258fd4..a6a15b697 100644 --- a/changelog/3695.bugfix.rst +++ b/changelog/3695.bugfix.rst @@ -1 +1 @@ -Fix ApproxNumpy initialisation argument mixup. abs rel tolerances were previously flipped +Fix ``ApproxNumpy`` initialisation argument mixup, ``abs`` and ``rel`` tolerances were flipped causing strange comparsion results. From d0ba242c46f2b4b08d7a02d302883b07906dcd08 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 27 Jul 2018 15:07:20 -0300 Subject: [PATCH 06/10] Implement change suggested by @kalekundert in PR --- src/_pytest/python_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 04022b9fe..6d2828a64 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -211,9 +211,7 @@ class ApproxScalar(ApproxBase): the pre-specified tolerance. """ if _is_numpy_array(actual): - import numpy as np - - return np.all(abs(self.expected - actual) <= self.tolerance) + return all(a == self for a in actual) # Short-circuit exact equality. if actual == self.expected: From bf127a63b2583b2c899fcaa4a38061e590711e21 Mon Sep 17 00:00:00 2001 From: Kale Kundert Date: Fri, 27 Jul 2018 11:24:42 -0700 Subject: [PATCH 07/10] Need to iterate over the flattened array. --- src/_pytest/python_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 6d2828a64..5331d8a84 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -211,7 +211,7 @@ class ApproxScalar(ApproxBase): the pre-specified tolerance. """ if _is_numpy_array(actual): - return all(a == self for a in actual) + return all(a == self for a in actual.flat) # Short-circuit exact equality. if actual == self.expected: From 330640eb96bb70d9c7ce01841b22a87904cdc703 Mon Sep 17 00:00:00 2001 From: abrammer Date: Sun, 29 Jul 2018 22:47:38 -0400 Subject: [PATCH 08/10] update tests to check tolerance args and expecing nan in numpy arrays --- testing/python/approx.py | 55 ++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/testing/python/approx.py b/testing/python/approx.py index 09487cc73..130547704 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -344,24 +344,51 @@ class TestApprox(object): def test_numpy_tolerance_args(self): """ - quick check that numpy rel/abs args are handled correctly + Check that numpy rel/abs args are handled correctly for comparison against an np.array - - 3.6.4 would approx both actual / expected if np.array - regardless of which value was passed to approx() - Means tolerance could be calculated against bad test return + Check both sides of the operator, hopefully it doesn't impact things. + Test all permutations of where the approx and np.array() can show up """ np = pytest.importorskip("numpy") - expected = 100 - actual = 99 - assert actual != pytest.approx(expected, abs=0.1, rel=0) - assert np.array(actual) != pytest.approx(expected, abs=0.1, rel=0) - assert actual != pytest.approx(np.array(expected), abs=0.1, rel=0) - assert np.array(actual) != pytest.approx(np.array(expected), abs=0.1, rel=0) + expected = 100. + actual = 99. + abs_diff = expected - actual + rel_diff = (expected - actual) / expected - assert actual == pytest.approx(expected, abs=0, rel=0.01) - assert np.array(actual) == pytest.approx(expected, abs=0, rel=0.01) - assert actual == pytest.approx(np.array(expected), abs=0, rel=0.01) - assert np.array(actual) == pytest.approx(np.array(expected), abs=0, rel=0.01) + tests = [ + (eq, abs_diff, 0), + (eq, 0, rel_diff), + (ne, 0, rel_diff / 2.), # rel diff fail + (ne, abs_diff / 2., 0), # abs diff fail + ] + + for op, _abs, _rel in tests: + assert op(np.array(actual), approx(expected, abs=_abs, rel=_rel)) # a, b + assert op(approx(expected, abs=_abs, rel=_rel), np.array(actual)) # b, a + + assert op(actual, approx(np.array(expected), abs=_abs, rel=_rel)) # a, b + assert op(approx(np.array(expected), abs=_abs, rel=_rel), actual) # b, a + + assert op(np.array(actual), approx(np.array(expected), abs=_abs, rel=_rel)) + assert op(approx(np.array(expected), abs=_abs, rel=_rel), np.array(actual)) + + def test_numpy_expecting_nan(self): + np = pytest.importorskip("numpy") + examples = [ + (eq, nan, nan), + (eq, -nan, -nan), + (eq, nan, -nan), + (ne, 0.0, nan), + (ne, inf, nan), + ] + for op, a, x in examples: + # Nothing is equal to NaN by default. + assert np.array(a) != approx(x) + assert a != approx(np.array(x)) + + # If ``nan_ok=True``, then NaN is equal to NaN. + assert op(np.array(a), approx(x, nan_ok=True)) + assert op(a, approx(np.array(x), nan_ok=True)) def test_numpy_array_wrong_shape(self): np = pytest.importorskip("numpy") From 762eaf443a72b4345bfd08cc423d3cf5c0a90224 Mon Sep 17 00:00:00 2001 From: abrammer Date: Sun, 29 Jul 2018 22:57:39 -0400 Subject: [PATCH 09/10] update changelog to include the addition of tests --- changelog/3695.bugfix.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog/3695.bugfix.rst b/changelog/3695.bugfix.rst index a6a15b697..42d07336b 100644 --- a/changelog/3695.bugfix.rst +++ b/changelog/3695.bugfix.rst @@ -1 +1,2 @@ Fix ``ApproxNumpy`` initialisation argument mixup, ``abs`` and ``rel`` tolerances were flipped causing strange comparsion results. +Add tests to check ``abs`` and ``rel`` tolerances for ``np.array`` and test for expecting ``nan`` with ``np.array()`` From 535fd1f3117e0f7d72abc80bfe1faef7033146b5 Mon Sep 17 00:00:00 2001 From: abrammer Date: Sun, 29 Jul 2018 23:12:04 -0400 Subject: [PATCH 10/10] may as well include inf test while we're at it --- testing/python/approx.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/testing/python/approx.py b/testing/python/approx.py index 130547704..0509fa672 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -390,6 +390,20 @@ class TestApprox(object): assert op(np.array(a), approx(x, nan_ok=True)) assert op(a, approx(np.array(x), nan_ok=True)) + def test_numpy_expecting_inf(self): + np = pytest.importorskip("numpy") + examples = [ + (eq, inf, inf), + (eq, -inf, -inf), + (ne, inf, -inf), + (ne, 0.0, inf), + (ne, nan, inf), + ] + for op, a, x in examples: + assert op(np.array(a), approx(x)) + assert op(a, approx(np.array(x))) + assert op(np.array(a), approx(np.array(x))) + def test_numpy_array_wrong_shape(self): np = pytest.importorskip("numpy")