Skip to content

format(Fraction(1, 3), '.016f') raises ValueError #130662

Closed

Description

@skirpichev

Bug report

Bug description:

c.f.

>>> format(float(Fraction(1, 3)), '.016f')
'0.3333333333333333'

Looking on docs, I think that float formatting better conforms to the specification.

Similar issue is valid for the width:

>>> format(float(Fraction(1, 3)), '0030.016f')
'0000000000000.3333333333333333'
>>> format(Fraction(1, 3), '0030.016f')
Traceback (most recent call last):
  File "<python-input-3>", line 1, in <module>
    format(Fraction(1, 3), '0030.016f')
    ~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.13/fractions.py", line 577, in __format__
    raise ValueError(
    ...<2 lines>...
    )
ValueError: Invalid format specifier '0030.016f' for object of type 'Fraction'

CPython versions tested on:

CPython main branch

Operating systems tested on:

No response

Linked PRs

Activity

  1. removed their assignment
    on Mar 4, 2025
  2. ant1sir commented on Mar 4, 2025

    @ant1sir
    Python 3.12.3 (main, Feb  4 2025, 14:48:35) [GCC 13.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import decimal
    >>> val = decimal.Decimal(1.25)
    >>> f"{val:06f}"
    '001.25'
    >>> decimal.__version__
    '1.70'
    
  3. serhiy-storchaka commented on Mar 5, 2025

    @serhiy-storchaka
    Member

    @rhettinger, @tim-one, what are your thoughts?

    There is a reason of not supporting leading zeroes in width in _GENERAL_FORMAT_SPECIFICATION_MATCHER -- because the first zero is the zeropad flag, but the zeropad flag is not supported here. This does not apply to _FLOAT_FORMAT_SPECIFICATION_MATCHER, where the zeropad flag is supported. I suspect that the pattern was just copied without further ado. I think that it would be good to harmonize format for float, Decimal and Fraction, and don't introduce a difference which is not necessary.

  4. serhiy-storchaka commented on Mar 6, 2025

    @serhiy-storchaka
    Member

    Yet one example: complex numbers. Leading zeroes are allowed in precision, but not allowed in width, because the zeropadding flag is not supported:

    >>> format(1j, '20.03f')
    '        0.000+1.000j'
    >>> format(1j, '020.3f')
    Traceback (most recent call last):
      File "<python-input-22>", line 1, in <module>
        format(1j, '020.3f')
        ~~~~~~^^^^^^^^^^^^^^
    ValueError: Zero padding is not allowed in complex format specifier
  5. mdickinson commented on Mar 6, 2025

    @mdickinson
    Member

    I suspect that the pattern was just copied without further ado.

    Just for the historical record, that's not what happened: the choice to exclude leading zeros for both the width and the precision was carefully considered and very much deliberate. That said, I'm happy to leave it to the active core devs to decide how to take this forward.

  6. skirpichev commented on Mar 10, 2025

    @skirpichev
    MemberAuthor

    For a context, this bug (and two other) is an outcome of the mpmath issue: f"{mpf('0.1'):.016f}" triggered a ValueError (format specifier parsing followed rather to Fraction's code). Lets focus on 'e' and 'f' format types first, I think that for 'g' format (or for None) we can accept more differences for Fraction's, float's and Decimal's.

    I think that it would be good to harmonize format for float, Decimal and Fraction, and don't introduce a difference which is not necessary.

    Exactly. Documenting all these tiny differences will be a nightmare both for documentation writers and for readers. And it's not clear for external projects (gmpy2/mpmath) to which convention follow.

    In my view, more permissible syntax for format specifiers is good for compatibility with other languages (read: C). That's all.

    More strict syntax rules (used in the fractions module and, partially, in the decimal module) - seems better (to me), as they are more economical and don't require explanation for some ambiguity ('0' flag vs leading zero in width field).

    So, #130717 seems better than #130663. I think that silent change (and a compatibility break) of the format specification language (without real changes in the code) is acceptable. I never seen those meaningless 0's in real code (maybe only something, coming from a typo).

  7. serhiy-storchaka commented on Mar 10, 2025

    @serhiy-storchaka
    Member

    Thank you for your information @mdickinson.

    Arguments for and against support of the leading zeros for the width and the precision are solid, but this left us with inconsistency. We should either allow or deprecate leading zeros in formats of all other types for consistency, for simplifying the documentation and the mental model.

    There is other issue related to leading zeros. The leading zero can be interpreted as the zeropad flag or as the fill character.

    >>> format(-1, '010')
    '-000000001'
    >>> format(-1, '>010')
    '00000000-1'

    The Decimal format is more restricted. The leading zero is only accepted as the fill character before the align flag or if there is no align flag. But any other fill character except zero and space require the align flag.

    >>> format(Decimal(-1), '010')
    '-000000001'
    >>> format(Decimal(-1), '0>10')
    '00000000-1'
    >>> format(Decimal(-1), '>010')
    Traceback (most recent call last):
      File "<python-input-77>", line 1, in <module>
        format(Decimal(-1), '>010')
        ~~~~~~^^^^^^^^^^^^^^^^^^^^^
    ValueError: invalid format string
    >>> format(Decimal(-1), '~10')
    Traceback (most recent call last):
      File "<python-input-78>", line 1, in <module>
        format(Decimal(-1), '~10')
        ~~~~~~^^^^^^^^^^^^^^^^^^^^
    ValueError: invalid format string
    >>> format(Decimal(-1), '~>10')
    '~~~~~~~~-1'
  8. skirpichev commented on Mar 10, 2025

    @skirpichev
    MemberAuthor

    The leading zero can be interpreted as the zeropad flag or as the fill character.

    I think that existing documentation is clear here: "When no explicit alignment is given, preceding the width field by a zero ('0') character enables sign-aware zero-padding for numeric types, excluding complex. This is equivalent to a fill character of '0' with an alignment type of '='."

    Are there reasons to prefer break this contract versus fixing Fraction/Decimal behavior?

    BTW, let me know if someone think this (alongside with other small format()'s incompatibilities for float/Fraction/Decimal) require a more wide discussion on DPO.

  9. added a commit that references this issue on Apr 6, 2025
  10. skirpichev commented on Apr 6, 2025

    @skirpichev
    MemberAuthor

    There is other issue related to leading zeros. The leading zero can be interpreted as the zeropad flag or as the fill character.

    @serhiy-storchaka, I think that issues with zeropad flag & fill/alignment should be treated separately, see #130716 and #131915. Lets focus on whether multiple leading zeros are allowed in width/precision.

    State of art:

    >>> format(float(0.25), '.02f')
    '0.25'
    >>> format(complex(0.25), '.02f')
    '0.25+0.00j'
    >>> format(Decimal(float(0.25)), '.02f')  # note that pure-Python version raises ValueError
    '0.25'
    >>> format(Fraction(float(0.25)), '.02f')
    Traceback (most recent call last):
      File "<python-input-11>", line 1, in <module>
        format(Fraction(float(0.25)), '.02f')
        ~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/sk/src/cpython/Lib/fractions.py", line 600, in __format__
        raise ValueError(
        ...<2 lines>...
        )
    ValueError: Invalid format specifier '.02f' for object of type 'Fraction'
    >>> format(float(0.25), '02f')
    '0.250000'
    >>> format(float(0.25), '002f')
    '0.250000'
    >>> format(complex(0.25), '02f')
    Traceback (most recent call last):
      File "<python-input-8>", line 1, in <module>
        format(complex(0.25), '02f')
        ~~~~~~^^^^^^^^^^^^^^^^^^^^^^
    ValueError: Zero padding is not allowed in complex format specifier
    >>> format(Decimal(float(0.25)), '02f')
    '0.25'
    >>> format(Decimal(float(0.25)), '002f')
    Traceback (most recent call last):
      File "<python-input-13>", line 1, in <module>
        format(Decimal(float(0.25)), '002f')
        ~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ValueError: invalid format string
    >>> format(Fraction(float(0.25)), '02f')
    '0.250000'
    >>> format(Fraction(float(0.25)), '002f')
    Traceback (most recent call last):
    ...
    ValueError: Invalid format specifier '002f' for object of type 'Fraction'

    So, it seems both pure-Python version of the Decimal type and the Fraction type - reject leading zeros. The C-code Decimal type allows leading zeros in precision field.

    CC @ericvsmith

  11. added a commit that references this issue on Apr 7, 2025
  12. skirpichev commented on Apr 15, 2025

    @skirpichev
    MemberAuthor
  13. vstinner commented on Apr 15, 2025

    @vstinner
    Member

    I agree with @serhiy-storchaka: #130662 (comment). I'm fine with accepting leading zeros in Fraction format spec for consistency with other numeric types (int, float, Decimal).

  14. added a commit that references this issue on Apr 15, 2025
  15. skirpichev commented on Apr 15, 2025

    @skirpichev
    MemberAuthor

    Ok, then PR #132549 - for Decimal's

  16. skirpichev commented on Apr 19, 2025

    @skirpichev
    MemberAuthor

    It seems, alternative proposal #130717 - not interested anyone, I'm closing this pr.

  17. added a commit that references this issue on Jun 2, 2025
  18. added a commit that references this issue on Jun 2, 2025
  19. added a commit that references this issue on Jun 2, 2025
  20. vstinner commented on Jun 2, 2025

    @vstinner
    Member

    It seems, alternative proposal #130717 - not interested anyone, I'm closing this pr.

    This grammar change seems to be useful, I suggest to reopen the PR.

  21. vstinner commented on Jun 2, 2025

    @vstinner
    Member

    I merged the two PRs for Fraction and Decimal.

  22. serhiy-storchaka commented on Jun 2, 2025

    @serhiy-storchaka
    Member

    Something related issues: accepting ASCII-only or non-ASCII digits and interpreting the single 0 as the zero-fill flag or the width.

  23. skirpichev commented on Jun 2, 2025

    @skirpichev
    MemberAuthor

    #135025 is related

  24. added 2 commits that reference this issue on Jul 7, 2025
  25. added 2 commits that reference this issue on Jul 7, 2025
  26. added 2 commits that reference this issue on Jul 12, 2025
  27. added 2 commits that reference this issue on Aug 4, 2025
  28. added 2 commits that reference this issue on Aug 19, 2025
  29. added 3 commits that reference this issue on Oct 26, 2026
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

    3.14bugs and security fixesstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions