4

A colleague and I just stumbled across an interesting problem using an f-string. Here is a minimal example:

>>> f"{ 42:x}"
'2a'

Writing a space after the hexadecimal type leads to a ValueError:

>>> f"{ 42:x }"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid format specifier

I understand the paragraph Leading and trailing whitespace in expressions is ignored in PEP 498 to mean that the space should actually be ignored.

Why does the space character lead to an error and what is the reasoning behind this?

khelwood
  • 55,782
  • 14
  • 81
  • 108
Stefan Scheller
  • 953
  • 1
  • 12
  • 22
  • 2
    The syntax is `{ }`, per PEP 498. `42` is the expression. `x` is a format specifier, and `:` a delimiter; they are not a part of the expression. – Amadan May 20 '22 at 13:04
  • 3
    A space could actually be a meaningful part of a format specifier, so spaces cannot be ignored. – khelwood May 20 '22 at 13:05
  • 1
    Thank you! I just looked into [PEP 3101](https://peps.python.org/pep-3101/#standard-format-specifiers) and " " is indeed a valid value for the "sign" option. – Stefan Scheller May 20 '22 at 13:23

1 Answers1

7

Per the link you shared:

For ease of readability, leading and trailing whitespace in expressions is ignored. This is a by-product of enclosing the expression in parentheses before evaluation.

The expression is everything[1] before the colon (:), while the format specifier is everything afterwards.

{ 42 : x } inside an f-string is equivalent to ( 42 ) with a format specifier of " x ".

( 42 ) == 42 but " x " != "x", and a format specifier is passed to the object's __format__ intact.

When the object attempts to format, it doesn't recognize the meaning of space (" ") in those positions, and throws an error.

Furthermore, on some format specifiers, space actually has a meaning, for example as a fill character or as a sign option:

>>> f"{42: >+5}"  # Width of 5, align to right, with space as fill and plus or minus as sign
'  +42'
>>> f"{42:#> 5}"  # Width of 5, align to right, with `#` as fill and space or minus as sign.
'## 42'

For more info, see the Format Specification Mini-Language.


1 Does not include the type conversion, for example !s, !r or !a.

Bharel
  • 23,672
  • 5
  • 40
  • 80
  • 1
    "The expression is everything before the colon" - Not quite. Type conversions aren't part of the expression, either, for example `f'{ 42 !s :10}'` is an [error](https://tio.run/##K6gsycjPM7YoKPr/v6AoM69EI029WsHESEGxWMHK0KBWXfP/fwA) as well. – Kelly Bundy May 20 '22 at 13:35
  • @KellyBundy Thank you. I've added a small footnote, as I don't wish the answer to be too complex. – Bharel May 20 '22 at 13:41