Skip to content

Commit 5fdb129

Browse files
encukouAA-Turnerblaisep
committed
Details & start on the formal grammar
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Blaise Pabon <blaise@gmail.com>
1 parent 366ecfc commit 5fdb129

File tree

2 files changed

+97
-117
lines changed

2 files changed

+97
-117
lines changed

Doc/library/stdtypes.rst

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,14 +2567,15 @@ field expression, the resulting f-string will contain the expression's source,
25672567
the equal sign, and the value of the expression.
25682568
This is often useful for debugging::
25692569

2570-
>>> print(f'{name=}')
2571-
name='Galahad'
2570+
>>> number = 14.3
2571+
>>> 'number=14.3'
2572+
number=14.3
25722573

2573-
Whitespace on both sides of the equal sign is significant --- it is retained
2574-
in the result::
2574+
Whitespace before, inside and after the expression, as well as whitespace
2575+
after the equal sign, is significant --- it is retained in the result::
25752576

2576-
>>> print(f'{name = }')
2577-
name = 'Galahad'
2577+
>>> f'{ number - 4 = }'
2578+
' number - 4 = 10.3'
25782579

25792580

25802581
Conversion specifier
@@ -2602,10 +2603,18 @@ The conversion can be specified explicitly using one of these specifiers:
26022603

26032604
For example::
26042605

2605-
>>> f'{one_third!r} is {one_third!s}'
2606-
'Fraction(1, 3) is 1/3'
2606+
>>> str(one_third)
2607+
'1/3'
2608+
>>> repr(one_third)
2609+
'Fraction(1, 3)'
2610+
2611+
>>> f'{one_third!s} is {one_third!r}'
2612+
'1/3 is Fraction(1, 3)'
26072613

26082614
>>> string = "¡kočka 😸!"
2615+
>>> ascii(string)
2616+
"'\\xa1ko\\u010dka \\U0001f638!'"
2617+
26092618
>>> f'{string = !a}'
26102619
"string = '\\xa1ko\\u010dka \\U0001f638!'"
26112620

Doc/reference/lexical_analysis.rst

Lines changed: 80 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,35 @@ assignment expressions ``:=`` must be surrounded by explicit parentheses::
981981
>>> f'{(half := 1/2)}, {half * 42}'
982982
'0.5, 21.0'
983983

984+
Reusing the outer f-string quoting type inside a replacement field is
985+
permitted::
986+
987+
>>> a = dict(x=2)
988+
>>> f"abc {a["x"]} def"
989+
'abc 2 def'
990+
991+
Backslashes are also allowed in replacement fields and are evaluated the same
992+
way as in any other context::
993+
994+
>>> a = ["a", "b", "c"]
995+
>>> print(f"List a contains:\n{"\n".join(a)}")
996+
List a contains:
997+
a
998+
b
999+
c
1000+
1001+
It is possible to nest f-strings::
1002+
1003+
>>> name = 'world'
1004+
>>> f'Repeated:{f' hello {name}' * 3}'
1005+
'Repeated: hello world hello world hello world'
1006+
1007+
Portable Python programs should not use more than 5 levels of nesting.
1008+
1009+
.. impl-detail::
1010+
1011+
CPython does not limit nesting of f-strings.
1012+
9841013
Replacement expressions can contain newlines in both single-quoted and
9851014
triple-quoted f-strings and they can contain comments.
9861015
Everything that comes after a ``#`` inside a replacement field
@@ -1010,15 +1039,16 @@ to the :func:`format` function to format a replacement field value.
10101039
For example, they can be used to specify a field width and padding characters
10111040
using the :ref:`Format Specification Mini-Language <formatspec>`::
10121041

1013-
>>> color = 'blue'
1014-
>>> f'{color:-^20s}'
1015-
'--------blue--------'
1042+
>>> number = 14.3
1043+
>>> f'{number:20.7f}'
1044+
' 14.3000000'
10161045

10171046
Top-level format specifiers may include nested replacement fields::
10181047

10191048
>>> field_size = 20
1020-
>>> f'{color:-^{field_size}s}'
1021-
'--------blue--------'
1049+
>>> precision = 7
1050+
>>> f'{number:{field_size}.{precision}f}'
1051+
' 14.3000000'
10221052

10231053
These nested fields may include their own conversion fields and
10241054
:ref:`format specifiers <formatspec>`::
@@ -1032,40 +1062,65 @@ These nested fields may include their own conversion fields and
10321062
However, these nested fields may not include more deeply nested replacement
10331063
fields.
10341064

1035-
Formatted string literals may be concatenated, but replacement fields
1036-
cannot be split across literals.
1037-
For example, the following is a single f-string::
1065+
Formatted string literals cannot be used as :term:`docstrings <docstring>`,
1066+
even if they do not include expressions::
10381067

1039-
>>> f'{' '}'
1040-
' '
1068+
>>> def foo():
1069+
... f"Not a docstring"
1070+
...
1071+
>>> print(foo.__doc__)
1072+
None
10411073

1042-
It is equivalent to ``f'{" "}'``, rather than ``f'{' "}"``.
1074+
.. seealso::
10431075

1076+
* :pep:`498` -- Literal String Interpolation
1077+
* :pep:`701` -- Syntactic formalization of f-strings
1078+
* :meth:`str.format`, which uses a related format string mechanism.
10441079

1045-
Formal grammar
1046-
^^^^^^^^^^^^^^
10471080

1048-
.. grammar-snippet:: python-grammar
1049-
:group: python-grammar
1081+
Formal grammar for f-strings
1082+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10501083

1051-
FSTRING_START: `fstringprefix` ("'" | '"' | "'''" | '"""')
1052-
FSTRING_MIDDLE:
1053-
| <any `source_character`, except backslash, newline, '{' and '}'>
1054-
| `stringescapeseq`
1055-
| "{{"
1056-
| "}}"
1057-
| <newline, in triple-quoted f-strings only>
1058-
FSTRING_END: ("'" | '"' | "'''" | '"""')
1059-
fstringprefix: <("f" | "fr" | "rf"), case-insensitive>
1060-
f_debug_specifier: whitespace* '=' whitespace*
1084+
F-strings are handled partly by the :term:`lexical analyzer`, which produces the
1085+
tokens :py:data:`~token.FSTRING_START`, :py:data:`~token.FSTRING_MIDDLE`
1086+
and :py:data:`~token.FSTRING_END`, and the parser, which handles expressions
1087+
in the replacement field.
1088+
The exact way the work is split is a CPython implementation detail.
1089+
1090+
Correspondingly, the f-string grammar is a mix of
1091+
:ref:`lexical and syntactic definitions <notation-lexical-vs-syntactic>`.
1092+
1093+
Whitespace is significant in these situations:
1094+
1095+
* There may be no whitespace in :py:data:`~token.FSTRING_START` (between
1096+
the prefix and quote).
1097+
* Whitespace in :py:data:`~token.FSTRING_MIDDLE` is part of the literal
1098+
string contents.
1099+
* In ``fstring_replacement_field``, if ``f_debug_specifier`` is present,
1100+
all whitespace after the opening brace up to the ``!`` of
1101+
``fstring_conversion``, ``:`` of ``fstring_full_format_spec``,
1102+
or the closing brace, is retained as part of the expression.
10611103

10621104
.. grammar-snippet:: python-grammar
10631105
:group: python-grammar
10641106

10651107
fstring: `FSTRING_START` `fstring_middle`* `FSTRING_END`
1108+
1109+
FSTRING_START: `fstringprefix` ("'" | '"' | "'''" | '"""')
1110+
FSTRING_END: `f_quote`
1111+
fstringprefix: <("f" | "fr" | "rf"), case-insensitive>
1112+
f_debug_specifier: '='
1113+
f_quote: <the quote character(s) used in FSTRING_START>
1114+
10661115
fstring_middle:
10671116
| `fstring_replacement_field`
10681117
| `FSTRING_MIDDLE`
1118+
FSTRING_MIDDLE:
1119+
| (!"\" !`newline` !'{' !'}' !`f_quote`) `source_character`
1120+
| `stringescapeseq`
1121+
| "{{"
1122+
| "}}"
1123+
| <newline, in triple-quoted f-strings only>
10691124
fstring_replacement_field:
10701125
| '{' `f_expression` [`f_debug_specifier`] [`fstring_conversion`]
10711126
[`fstring_full_format_spec`] '}'
@@ -1081,90 +1136,6 @@ Formal grammar
10811136
| `yield_expression`
10821137
10831138

1084-
.. versionchanged:: 3.7
1085-
Prior to Python 3.7, an :keyword:`await` expression and comprehensions
1086-
containing an :keyword:`async for` clause were illegal in the expressions
1087-
in formatted string literals due to a problem with the implementation.
1088-
1089-
.. versionchanged:: 3.12
1090-
Prior to Python 3.12, comments were not allowed inside f-string replacement
1091-
fields.
1092-
1093-
---------------
1094-
1095-
1096-
Some examples of formatted string literals::
1097-
1098-
>>> name = "Fred"
1099-
>>> f"He said his name is {name!r}."
1100-
"He said his name is 'Fred'."
1101-
>>> f"He said his name is {repr(name)}." # repr() is equivalent to !r
1102-
"He said his name is 'Fred'."
1103-
>>> width = 10
1104-
>>> precision = 4
1105-
>>> value = decimal.Decimal("12.34567")
1106-
>>> f"result: {value:{width}.{precision}}" # nested fields
1107-
'result: 12.35'
1108-
>>> today = datetime(year=2017, month=1, day=27)
1109-
>>> f"{today:%B %d, %Y}" # using date format specifier
1110-
'January 27, 2017'
1111-
>>> f"{today=:%B %d, %Y}" # using date format specifier and debugging
1112-
'today=January 27, 2017'
1113-
>>> number = 1024
1114-
>>> f"{number:#0x}" # using integer format specifier
1115-
'0x400'
1116-
>>> foo = "bar"
1117-
>>> f"{ foo = }" # preserves whitespace
1118-
" foo = 'bar'"
1119-
>>> line = "The mill's closed"
1120-
>>> f"{line = }"
1121-
'line = "The mill\'s closed"'
1122-
>>> f"{line = :20}"
1123-
"line = The mill's closed "
1124-
>>> f"{line = !r:20}"
1125-
'line = "The mill\'s closed" '
1126-
1127-
1128-
Reusing the outer f-string quoting type inside a replacement field is
1129-
permitted::
1130-
1131-
>>> a = dict(x=2)
1132-
>>> f"abc {a["x"]} def"
1133-
'abc 2 def'
1134-
1135-
.. versionchanged:: 3.12
1136-
Prior to Python 3.12, reuse of the same quoting type of the outer f-string
1137-
inside a replacement field was not possible.
1138-
1139-
Backslashes are also allowed in replacement fields and are evaluated the same
1140-
way as in any other context::
1141-
1142-
>>> a = ["a", "b", "c"]
1143-
>>> print(f"List a contains:\n{"\n".join(a)}")
1144-
List a contains:
1145-
a
1146-
b
1147-
c
1148-
1149-
.. versionchanged:: 3.12
1150-
Prior to Python 3.12, backslashes were not permitted inside an f-string
1151-
replacement field.
1152-
1153-
Formatted string literals cannot be used as docstrings, even if they do not
1154-
include expressions.
1155-
1156-
::
1157-
1158-
>>> def foo():
1159-
... f"Not a docstring"
1160-
...
1161-
>>> foo.__doc__ is None
1162-
True
1163-
1164-
See also :pep:`498` for the proposal that added formatted string literals,
1165-
and :meth:`str.format`, which uses a related format string mechanism.
1166-
1167-
11681139
.. _t-strings:
11691140
.. _template-string-literals:
11701141

0 commit comments

Comments
 (0)