Skip to content

Commit

Permalink
Improve approx repr for better readability (#12665)
Browse files Browse the repository at this point in the history
Adjust `ApproxScalar.__repr__` to format tolerances in decimal form for smaller ranges.

Closes #6985

---------

Co-authored-by: Bruno Oliveira <[email protected]>
Co-authored-by: Pierre Sassoulas <[email protected]>
  • Loading branch information
3 people authored Aug 1, 2024
1 parent f0a0436 commit 5a01583
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 6 deletions.
21 changes: 21 additions & 0 deletions changelog/6985.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Improved :func:`pytest.approx` to enhance the readability of value ranges and tolerances between 0.001 and 1000.
* The `repr` method now provides clearer output for values within those ranges, making it easier to interpret the results.
* Previously, the output for those ranges of values and tolerances was displayed in scientific notation (e.g., `42 ± 1.0e+00`). The updated method now presents the tolerance as a decimal for better readability (e.g., `42 ± 1`).

Example:

**Previous Output:**

.. code-block:: console
>>> pytest.approx(42, abs=1)
42 ± 1.0e+00
**Current Output:**

.. code-block:: console
>>> pytest.approx(42, abs=1)
42 ± 1
-- by :user:`fazeelghafoor`
6 changes: 5 additions & 1 deletion src/_pytest/python_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,11 @@ def __repr__(self) -> str:
# If a sensible tolerance can't be calculated, self.tolerance will
# raise a ValueError. In this case, display '???'.
try:
vetted_tolerance = f"{self.tolerance:.1e}"
if 1e-3 <= self.tolerance < 1e3:
vetted_tolerance = f"{self.tolerance:n}"
else:
vetted_tolerance = f"{self.tolerance:.1e}"

if (
isinstance(self.expected, Complex)
and self.expected.imag
Expand Down
16 changes: 11 additions & 5 deletions testing/python/approx.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def do_assert(lhs, rhs, expected_message, verbosity_level=0):

SOME_FLOAT = r"[+-]?([0-9]*[.])?[0-9]+\s*"
SOME_INT = r"[0-9]+\s*"
SOME_TOLERANCE = rf"({SOME_FLOAT}|[+-]?[0-9]+(\.[0-9]+)?[eE][+-]?[0-9]+\s*)"


class TestApprox:
Expand All @@ -103,7 +104,7 @@ def test_error_messages_native_dtypes(self, assert_approx_raises_regex):
"",
" comparison failed",
f" Obtained: {SOME_FLOAT}",
f" Expected: {SOME_FLOAT} ± {SOME_FLOAT}",
f" Expected: {SOME_FLOAT} ± {SOME_TOLERANCE}",
],
)

Expand All @@ -119,9 +120,9 @@ def test_error_messages_native_dtypes(self, assert_approx_raises_regex):
r" comparison failed. Mismatched elements: 2 / 3:",
rf" Max absolute difference: {SOME_FLOAT}",
rf" Max relative difference: {SOME_FLOAT}",
r" Index \| Obtained\s+\| Expected ",
rf" a \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
rf" c \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
r" Index \| Obtained\s+\| Expected\s+",
rf" a \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_TOLERANCE}",
rf" c \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_TOLERANCE}",
],
)

Expand Down Expand Up @@ -334,6 +335,11 @@ def test_repr_string(self):
"approx({'b': 2.0 ± 2.0e-06, 'a': 1.0 ± 1.0e-06})",
)

assert repr(approx(42, abs=1)) == "42 ± 1"
assert repr(approx(5, rel=0.01)) == "5 ± 0.05"
assert repr(approx(24000, abs=500)) == "24000 ± 500"
assert repr(approx(1500, abs=555)) == "1500 ± 555"

def test_repr_complex_numbers(self):
assert repr(approx(inf + 1j)) == "(inf+1j)"
assert repr(approx(1.0j, rel=inf)) == "1j ± inf"
Expand All @@ -347,7 +353,7 @@ def test_repr_complex_numbers(self):
assert repr(approx(3 + 4 * 1j)) == "(3+4j) ± 5.0e-06 ∠ ±180°"

# absolute tolerance is not scaled
assert repr(approx(3.3 + 4.4 * 1j, abs=0.02)) == "(3.3+4.4j) ± 2.0e-02 ∠ ±180°"
assert repr(approx(3.3 + 4.4 * 1j, abs=0.02)) == "(3.3+4.4j) ± 0.02 ∠ ±180°"

@pytest.mark.parametrize(
"value, expected_repr_string",
Expand Down

0 comments on commit 5a01583

Please sign in to comment.