You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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'
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.
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
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.
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).
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.
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'
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.
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.
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).
Activity
ant1sir commentedon Mar 4, 2025
serhiy-storchaka commentedon Mar 5, 2025
@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.
serhiy-storchaka commentedon Mar 6, 2025
Yet one example: complex numbers. Leading zeroes are allowed in precision, but not allowed in width, because the zeropadding flag is not supported:
mdickinson commentedon Mar 6, 2025
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.
skirpichev commentedon Mar 10, 2025
For a context, this bug (and two other) is an outcome of the mpmath issue:
f"{mpf('0.1'):.016f}"triggered aValueError(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 forNone) we can accept more differences for Fraction's, float's and Decimal's.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).
serhiy-storchaka commentedon Mar 10, 2025
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.
The
Decimalformat 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.skirpichev commentedon Mar 10, 2025
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.
Revert 849e185 and (partially) f4cef9c (same for with field)
skirpichev commentedon Apr 6, 2025
@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:
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
Revert 849e185 and (partially) f4cef9c (same for with field)
skirpichev commentedon Apr 15, 2025
CC @vstinner
vstinner commentedon Apr 15, 2025
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).
pythongh-130662: accept leading zeros in precision/width for Decimal'…
skirpichev commentedon Apr 15, 2025
Ok, then PR #132549 - for Decimal's
skirpichev commentedon Apr 19, 2025
It seems, alternative proposal #130717 - not interested anyone, I'm closing this pr.
gh-130662: Accept leading zeros in precision/width for Fraction's for…
pythongh-130662: Accept leading zeros in precision/width for Fraction…
gh-130662: Accept leading zeros in precision/width for Decimal's form…
vstinner commentedon Jun 2, 2025
This grammar change seems to be useful, I suggest to reopen the PR.
vstinner commentedon Jun 2, 2025
I merged the two PRs for Fraction and Decimal.
serhiy-storchaka commentedon Jun 2, 2025
Something related issues: accepting ASCII-only or non-ASCII digits and interpreting the single 0 as the zero-fill flag or the width.
skirpichev commentedon Jun 2, 2025
#135025 is related
bintime: Fix TimeDelta.__repr__ formatting errors with PyPy
nitypes: Enable testing with PyPy (#134)
pythongh-130662: Accept leading zeros in precision/width for Fraction…
pythongh-130662: Accept leading zeros in precision/width for Decimal'…
[3.14] gh-130662: Accept leading zeros in precision/width for Fractio…
[3.14] gh-130662: Accept leading zeros in precision/width for Decimal…
pythongh-130662: Accept leading zeros in precision/width for Fraction…
pythongh-130662: Accept leading zeros in precision/width for Decimal'…
pythongh-130662: Accept leading zeros in precision/width for Fraction…
pythongh-130662: Accept leading zeros in precision/width for Decimal'…
pythongh-130662: Accept leading zeros in precision/width for Fraction…
pythongh-130662: Accept leading zeros in precision/width for Decimal'…
gh-130662: Remove unnecessary plus sign from a news entry (#160723)
[3.15] gh-130662: Remove unnecessary plus sign from a news entry (GH-…
[3.14] gh-130662: Remove unnecessary plus sign from a news entry (#16…