parent
bb94e83b49
commit
96412d19ab
1
AUTHORS
1
AUTHORS
|
@ -361,5 +361,6 @@ Yoav Caspi
|
||||||
Yuval Shimon
|
Yuval Shimon
|
||||||
Zac Hatfield-Dodds
|
Zac Hatfield-Dodds
|
||||||
Zachary Kneupper
|
Zachary Kneupper
|
||||||
|
Zachary OBrien
|
||||||
Zoltán Máté
|
Zoltán Máté
|
||||||
Zsolt Cserna
|
Zsolt Cserna
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fixed string representation for :func:`pytest.approx` when used to compare tuples.
|
|
@ -133,9 +133,11 @@ class ApproxBase:
|
||||||
# raise if there are any non-numeric elements in the sequence.
|
# raise if there are any non-numeric elements in the sequence.
|
||||||
|
|
||||||
|
|
||||||
def _recursive_list_map(f, x):
|
def _recursive_sequence_map(f, x):
|
||||||
if isinstance(x, list):
|
"""Recursively map a function over a sequence of arbitary depth"""
|
||||||
return [_recursive_list_map(f, xi) for xi in x]
|
if isinstance(x, (list, tuple)):
|
||||||
|
seq_type = type(x)
|
||||||
|
return seq_type(_recursive_sequence_map(f, xi) for xi in x)
|
||||||
else:
|
else:
|
||||||
return f(x)
|
return f(x)
|
||||||
|
|
||||||
|
@ -144,7 +146,9 @@ class ApproxNumpy(ApproxBase):
|
||||||
"""Perform approximate comparisons where the expected value is numpy array."""
|
"""Perform approximate comparisons where the expected value is numpy array."""
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
|
list_scalars = _recursive_sequence_map(
|
||||||
|
self._approx_scalar, self.expected.tolist()
|
||||||
|
)
|
||||||
return f"approx({list_scalars!r})"
|
return f"approx({list_scalars!r})"
|
||||||
|
|
||||||
def _repr_compare(self, other_side: "ndarray") -> List[str]:
|
def _repr_compare(self, other_side: "ndarray") -> List[str]:
|
||||||
|
@ -164,7 +168,7 @@ class ApproxNumpy(ApproxBase):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
np_array_shape = self.expected.shape
|
np_array_shape = self.expected.shape
|
||||||
approx_side_as_list = _recursive_list_map(
|
approx_side_as_seq = _recursive_sequence_map(
|
||||||
self._approx_scalar, self.expected.tolist()
|
self._approx_scalar, self.expected.tolist()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -179,7 +183,7 @@ class ApproxNumpy(ApproxBase):
|
||||||
max_rel_diff = -math.inf
|
max_rel_diff = -math.inf
|
||||||
different_ids = []
|
different_ids = []
|
||||||
for index in itertools.product(*(range(i) for i in np_array_shape)):
|
for index in itertools.product(*(range(i) for i in np_array_shape)):
|
||||||
approx_value = get_value_from_nested_list(approx_side_as_list, index)
|
approx_value = get_value_from_nested_list(approx_side_as_seq, index)
|
||||||
other_value = get_value_from_nested_list(other_side, index)
|
other_value = get_value_from_nested_list(other_side, index)
|
||||||
if approx_value != other_value:
|
if approx_value != other_value:
|
||||||
abs_diff = abs(approx_value.expected - other_value)
|
abs_diff = abs(approx_value.expected - other_value)
|
||||||
|
@ -194,7 +198,7 @@ class ApproxNumpy(ApproxBase):
|
||||||
(
|
(
|
||||||
str(index),
|
str(index),
|
||||||
str(get_value_from_nested_list(other_side, index)),
|
str(get_value_from_nested_list(other_side, index)),
|
||||||
str(get_value_from_nested_list(approx_side_as_list, index)),
|
str(get_value_from_nested_list(approx_side_as_seq, index)),
|
||||||
)
|
)
|
||||||
for index in different_ids
|
for index in different_ids
|
||||||
]
|
]
|
||||||
|
@ -326,7 +330,7 @@ class ApproxSequenceLike(ApproxBase):
|
||||||
f"Lengths: {len(self.expected)} and {len(other_side)}",
|
f"Lengths: {len(self.expected)} and {len(other_side)}",
|
||||||
]
|
]
|
||||||
|
|
||||||
approx_side_as_map = _recursive_list_map(self._approx_scalar, self.expected)
|
approx_side_as_map = _recursive_sequence_map(self._approx_scalar, self.expected)
|
||||||
|
|
||||||
number_of_elements = len(approx_side_as_map)
|
number_of_elements = len(approx_side_as_map)
|
||||||
max_abs_diff = -math.inf
|
max_abs_diff = -math.inf
|
||||||
|
|
|
@ -2,12 +2,14 @@ import operator
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
from math import sqrt
|
||||||
from operator import eq
|
from operator import eq
|
||||||
from operator import ne
|
from operator import ne
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
|
from _pytest.python_api import _recursive_sequence_map
|
||||||
from pytest import approx
|
from pytest import approx
|
||||||
|
|
||||||
inf, nan = float("inf"), float("nan")
|
inf, nan = float("inf"), float("nan")
|
||||||
|
@ -133,6 +135,18 @@ class TestApprox:
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert_approx_raises_regex(
|
||||||
|
(1, 2.2, 4),
|
||||||
|
(1, 3.2, 4),
|
||||||
|
[
|
||||||
|
r" comparison failed. Mismatched elements: 1 / 3:",
|
||||||
|
rf" Max absolute difference: {SOME_FLOAT}",
|
||||||
|
rf" Max relative difference: {SOME_FLOAT}",
|
||||||
|
r" Index \| Obtained\s+\| Expected ",
|
||||||
|
rf" 1 \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
# Specific test for comparison with 0.0 (relative diff will be 'inf')
|
# Specific test for comparison with 0.0 (relative diff will be 'inf')
|
||||||
assert_approx_raises_regex(
|
assert_approx_raises_regex(
|
||||||
[0.0],
|
[0.0],
|
||||||
|
@ -878,3 +892,31 @@ class TestApprox:
|
||||||
"""pytest.approx() should raise an error on unordered sequences (#9692)."""
|
"""pytest.approx() should raise an error on unordered sequences (#9692)."""
|
||||||
with pytest.raises(TypeError, match="only supports ordered sequences"):
|
with pytest.raises(TypeError, match="only supports ordered sequences"):
|
||||||
assert {1, 2, 3} == approx({1, 2, 3})
|
assert {1, 2, 3} == approx({1, 2, 3})
|
||||||
|
|
||||||
|
|
||||||
|
class TestRecursiveSequenceMap:
|
||||||
|
def test_map_over_scalar(self):
|
||||||
|
assert _recursive_sequence_map(sqrt, 16) == 4
|
||||||
|
|
||||||
|
def test_map_over_empty_list(self):
|
||||||
|
assert _recursive_sequence_map(sqrt, []) == []
|
||||||
|
|
||||||
|
def test_map_over_list(self):
|
||||||
|
assert _recursive_sequence_map(sqrt, [4, 16, 25, 676]) == [2, 4, 5, 26]
|
||||||
|
|
||||||
|
def test_map_over_tuple(self):
|
||||||
|
assert _recursive_sequence_map(sqrt, (4, 16, 25, 676)) == (2, 4, 5, 26)
|
||||||
|
|
||||||
|
def test_map_over_nested_lists(self):
|
||||||
|
assert _recursive_sequence_map(sqrt, [4, [25, 64], [[49]]]) == [
|
||||||
|
2,
|
||||||
|
[5, 8],
|
||||||
|
[[7]],
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_map_over_mixed_sequence(self):
|
||||||
|
assert _recursive_sequence_map(sqrt, [4, (25, 64), [(49)]]) == [
|
||||||
|
2,
|
||||||
|
(5, 8),
|
||||||
|
[(7)],
|
||||||
|
]
|
||||||
|
|
Loading…
Reference in New Issue