diff --git a/changelog/6985.improvement.rst b/changelog/6985.improvement.rst new file mode 100644 index 00000000000..34ee8edc77d --- /dev/null +++ b/changelog/6985.improvement.rst @@ -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` diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 4174a55b589..d2107c2fc78 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -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 diff --git a/testing/python/approx.py b/testing/python/approx.py index 69743cdbe17..21932059cc6 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -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: @@ -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}", ], ) @@ -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}", ], ) @@ -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" @@ -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",