Skip to content

pytest fails with python 3.12b4 possibly due to Fraction formatting change #1818

Closed

Description

@mtasaka

With python 3.12b4 (actually on Fedora 39, python3-3.12.0~b4-1.fc39.x86_64), pytest for pint git head ( 2d649ea ) fails as below:

$ pytest --benchmark-skip
============================= test session starts ==============================
platform linux -- Python 3.12.0b4, pytest-7.3.2, pluggy-1.0.0
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
Matplotlib: 3.7.2
Freetype: 2.13.1
rootdir: /builddir/build/BUILD/GIT/pint
plugins: cov-4.0.0, benchmark-4.0.0, subtests-0.10.0, mpl-0.13
collected 2536 items / 3 skipped
....
....
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeQuantityFraction::test_to_base_units
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication[input_tuple0-expected_output0]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication[input_tuple21-expected_output21]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication[input_tuple28-expected_output28]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication[input_tuple35-expected_output35]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_autoconvert[input_tuple0-expected_output0]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_autoconvert[input_tuple1-expected_output1]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_autoconvert[input_tuple2-expected_output2]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_autoconvert[input_tuple3-expected_output3]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_autoconvert[input_tuple4-expected_output4]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_autoconvert[input_tuple8-expected_output8]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_autoconvert[input_tuple9-expected_output9]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_autoconvert[input_tuple10-expected_output10]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_multiplication_with_scalar[input_tuple1-expected_output1]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_division_with_scalar[input_tuple1-expected_output1]
FAILED pint/testsuite/test_non_int.py::TestNonIntTypeOffsetUnitMathFraction::test_exponentiation[input_tuple3-expected_output3]
= 16 failed, 2009 passed, 503 skipped, 11 xfailed, 6 warnings, 298 subtests passed in 182.14s (0:03:02) =

... and all of the above failures are like:

______________ TestNonIntTypeQuantityFraction.test_to_base_units _______________
self = <pint.testsuite.test_non_int.TestNonIntTypeQuantityFraction object at 0x7f1635287b00>

    def test_to_base_units(self):
        x = self.Q_("1*inch")
        self.assert_quantity_almost_equal(
            x.to_base_units(), self.QP_("0.0254", "meter")
        )
        x = self.Q_("1*inch*inch")
>       self.assert_quantity_almost_equal(
            x.to_base_units(),
            self.Q_(
                self.kwargs["non_int_type"]("0.0254")
                ** self.kwargs["non_int_type"]("2.0"),
                "meter*meter",
            ),
        )

pint/testsuite/test_non_int.py:159: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
pint/testsuite/test_non_int.py:29: in assert_quantity_almost_equal
    helpers.assert_quantity_almost_equal(
pint/testing.py:66: in assert_allclose
    msg = f"Comparing {first!r} and {second!r}. "
pint/facets/plain/quantity.py:262: in __repr__
    return f"<Quantity({self._magnitude}, '{self._units}')>"
pint/util.py:608: in __format__
    return format_unit(self, spec)
pint/formatting.py:452: in format_unit
    return fmt(unit, registry=registry, **options)
pint/formatting.py:268: in format_default
    return formatter(
pint/formatting.py:386: in formatter
    pos_terms.append(power_fmt.format(key, fun(value)))
pint/formatting.py:349: in <lambda>
    fun = lambda x: exp_call(abs(x))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = Fraction(2, 1), format_spec = 'n'

    def __format__(self, format_spec, /):
        """Format this fraction according to the given format specification."""
    
        # Backwards compatiblility with existing formatting.
        if not format_spec:
            return str(self)
    
        # Validate and parse the format specifier.
        match = _FLOAT_FORMAT_SPECIFICATION_MATCHER(format_spec)
        if match is None:
>           raise ValueError(
                f"Invalid format specifier {format_spec!r} "
                f"for object of type {type(self).__name__!r}"
E               ValueError: Invalid format specifier 'n' for object of type 'Fraction'

/usr/lib64/python3.12/fractions.py:427: ValueError

Activity

  1. mtasaka commented on Jul 16, 2023

    @mtasaka
    ContributorAuthor

    I believe this is due to python3.12 formatting change for Fraction.

    With python 3.11:

    $ python3
    Python 3.11.4 (main, Jun  7 2023, 00:00:00) [GCC 13.1.1 20230511 (Red Hat 13.1.1-2)] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from fractions import Fraction
    >>> frac = Fraction(4,5)
    >>> "{:n}".format(4.0 / 5)
    '0.8'
    >>> "{:n}".format(frac)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unsupported format string passed to Fraction.__format__
    

    With python 3.12b4:

    $ python3
    Python 3.12.0b4 (main, Jul 12 2023, 00:00:00) [GCC 13.1.1 20230614 (Red Hat 13.1.1-4)] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from fractions import Fraction
    >>> frac = Fraction(4,5)
    >>> "{:n}".format(4.0 / 5)
    '0.8'
    >>> "{:n}".format(frac)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib64/python3.12/fractions.py", line 427, in __format__
        raise ValueError(
    ValueError: Invalid format specifier 'n' for object of type 'Fraction'
    

    So python 3.11 raised TypeError, now python3.12b4 raises ValueError, possibly due to python/cpython@3e09f31#diff-f561eb7eb97f832b2698837f52c2c2cf23bdb0b31c69cf1f6aaa560280993316R417 .
    Looking at the backtrace, it seems that pint is expected to catch this error at:

    except TypeError:
    (actually with python 3.11 the above TypeError was caught here), however with python 3.12b4 this fails.

  2. mtasaka commented on Jul 16, 2023

    @mtasaka
    ContributorAuthor

    The above errors are all resolved by the following (not knowing this is desired, although)

    diff --git a/pint/testing.py b/pint/testing.py
    index 126a39f..f2a570a 100644
    --- a/pint/testing.py
    +++ b/pint/testing.py
    @@ -64,7 +64,7 @@ def assert_allclose(
         if msg is None:
             try:
                 msg = f"Comparing {first!r} and {second!r}. "
    -        except TypeError:
    +        except (TypeError, ValueError):
                 try:
                     msg = f"Comparing {first} and {second}. "
                 except Exception:
    
  3. hgrecco commented on Jul 17, 2023

    @hgrecco
    Owner

    Thanks for catching this. Would you mind issuing a PR?

  4. added a commit that references this issue on Jul 17, 2023
    2bd6677
  5. added a commit that references this issue on Apr 16, 2025
    aa314de
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions