gh-135676: Reword the f-string (and t-string) section (GH-137469)

Much of the information was duplicated in stdtypes.rst; this PR keeps lexical/syntactical details in Lexical Analysis and the evaluation & runtime behaviour in Standard types, with cross-references between the two.
Since the t-string section only listed differences from f-strings, and the grammar for the two is equivalent, that section was moved to Standard types almost entirely.

Co-authored-by: Blaise Pabon <blaise@gmail.com>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com>
This commit is contained in:
Petr Viktorin 2025-12-03 16:14:53 +01:00 committed by GitHub
parent f6f456f950
commit aea5531583
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 323 additions and 234 deletions

View file

@ -2656,6 +2656,8 @@ expression support in the :mod:`re` module).
single: : (colon); in formatted string literal single: : (colon); in formatted string literal
single: = (equals); for help in debugging using string literals single: = (equals); for help in debugging using string literals
.. _stdtypes-fstrings:
Formatted String Literals (f-strings) Formatted String Literals (f-strings)
------------------------------------- -------------------------------------
@ -2664,123 +2666,147 @@ Formatted String Literals (f-strings)
The :keyword:`await` and :keyword:`async for` can be used in expressions The :keyword:`await` and :keyword:`async for` can be used in expressions
within f-strings. within f-strings.
.. versionchanged:: 3.8 .. versionchanged:: 3.8
Added the debugging operator (``=``) Added the debug specifier (``=``)
.. versionchanged:: 3.12 .. versionchanged:: 3.12
Many restrictions on expressions within f-strings have been removed. Many restrictions on expressions within f-strings have been removed.
Notably, nested strings, comments, and backslashes are now permitted. Notably, nested strings, comments, and backslashes are now permitted.
An :dfn:`f-string` (formally a :dfn:`formatted string literal`) is An :dfn:`f-string` (formally a :dfn:`formatted string literal`) is
a string literal that is prefixed with ``f`` or ``F``. a string literal that is prefixed with ``f`` or ``F``.
This type of string literal allows embedding arbitrary Python expressions This type of string literal allows embedding the results of arbitrary Python
within *replacement fields*, which are delimited by curly brackets (``{}``). expressions within *replacement fields*, which are delimited by curly
These expressions are evaluated at runtime, similarly to :meth:`str.format`, brackets (``{}``).
and are converted into regular :class:`str` objects. Each replacement field must contain an expression, optionally followed by:
For example:
.. doctest:: * a *debug specifier* -- an equal sign (``=``);
* a *conversion specifier* -- ``!s``, ``!r`` or ``!a``; and/or
* a *format specifier* prefixed with a colon (``:``).
>>> who = 'nobody' See the :ref:`Lexical Analysis section on f-strings <f-strings>` for details
>>> nationality = 'Spanish' on the syntax of these fields.
>>> f'{who.title()} expects the {nationality} Inquisition!'
'Nobody expects the Spanish Inquisition!'
It is also possible to use a multi line f-string: Debug specifier
^^^^^^^^^^^^^^^
.. doctest:: .. versionadded:: 3.8
>>> f'''This is a string If a debug specifier -- an equal sign (``=``) -- appears after the replacement
... on two lines''' field expression, the resulting f-string will contain the expression's source,
'This is a string\non two lines' the equal sign, and the value of the expression.
This is often useful for debugging::
A single opening curly bracket, ``'{'``, marks a *replacement field* that >>> number = 14.3
can contain any Python expression: >>> f'{number=}'
'number=14.3'
.. doctest:: Whitespace before, inside and after the expression, as well as whitespace
after the equal sign, is significant --- it is retained in the result::
>>> nationality = 'Spanish' >>> f'{ number - 4 = }'
>>> f'The {nationality} Inquisition!' ' number - 4 = 10.3'
'The Spanish Inquisition!'
To include a literal ``{`` or ``}``, use a double bracket:
.. doctest:: Conversion specifier
^^^^^^^^^^^^^^^^^^^^
>>> x = 42 By default, the value of a replacement field expression is converted to
>>> f'{{x}} is {x}' a string using :func:`str`::
'{x} is 42'
Functions can also be used, and :ref:`format specifiers <formatstrings>`:
.. doctest::
>>> from math import sqrt
>>> f'√2 \N{ALMOST EQUAL TO} {sqrt(2):.5f}'
'√2 ≈ 1.41421'
Any non-string expression is converted using :func:`str`, by default:
.. doctest::
>>> from fractions import Fraction >>> from fractions import Fraction
>>> f'{Fraction(1, 3)}' >>> one_third = Fraction(1, 3)
>>> f'{one_third}'
'1/3' '1/3'
To use an explicit conversion, use the ``!`` (exclamation mark) operator, When a debug specifier but no format specifier is used, the default conversion
followed by any of the valid formats, which are: instead uses :func:`repr`::
========== ============== >>> f'{one_third = }'
Conversion Meaning 'one_third = Fraction(1, 3)'
========== ==============
``!a`` :func:`ascii`
``!r`` :func:`repr`
``!s`` :func:`str`
========== ==============
For example: The conversion can be specified explicitly using one of these specifiers:
.. doctest:: * ``!s`` for :func:`str`
* ``!r`` for :func:`repr`
* ``!a`` for :func:`ascii`
>>> from fractions import Fraction For example::
>>> f'{Fraction(1, 3)!s}'
>>> str(one_third)
'1/3' '1/3'
>>> f'{Fraction(1, 3)!r}' >>> repr(one_third)
'Fraction(1, 3)' 'Fraction(1, 3)'
>>> question = '¿Dónde está el Presidente?'
>>> print(f'{question!a}')
'\xbfD\xf3nde est\xe1 el Presidente?'
While debugging it may be helpful to see both the expression and its value, >>> f'{one_third!s} is {one_third!r}'
by using the equals sign (``=``) after the expression. '1/3 is Fraction(1, 3)'
This preserves spaces within the brackets, and can be used with a converter.
By default, the debugging operator uses the :func:`repr` (``!r``) conversion.
For example:
.. doctest:: >>> string = "¡kočka 😸!"
>>> ascii(string)
"'\\xa1ko\\u010dka \\U0001f638!'"
>>> f'{string = !a}'
"string = '\\xa1ko\\u010dka \\U0001f638!'"
Format specifier
^^^^^^^^^^^^^^^^
After the expression has been evaluated, and possibly converted using an
explicit conversion specifier, it is formatted using the :func:`format` function.
If the replacement field includes a *format specifier* introduced by a colon
(``:``), the specifier is passed to :func:`!format` as the second argument.
The result of :func:`!format` is then used as the final value for the
replacement field. For example::
>>> from fractions import Fraction >>> from fractions import Fraction
>>> calculation = Fraction(1, 3) >>> one_third = Fraction(1, 3)
>>> f'{calculation=}' >>> f'{one_third:.6f}'
'calculation=Fraction(1, 3)' '0.333333'
>>> f'{calculation = }' >>> f'{one_third:_^+10}'
'calculation = Fraction(1, 3)' '___+1/3___'
>>> f'{calculation = !s}' >>> >>> f'{one_third!r:_^20}'
'calculation = 1/3' '___Fraction(1, 3)___'
>>> f'{one_third = :~>10}~'
'one_third = ~~~~~~~1/3~'
Once the output has been evaluated, it can be formatted using a .. _stdtypes-tstrings:
:ref:`format specifier <formatstrings>` following a colon (``':'``).
After the expression has been evaluated, and possibly converted to a string,
the :meth:`!__format__` method of the result is called with the format specifier,
or the empty string if no format specifier is given.
The formatted result is then used as the final value for the replacement field.
For example:
.. doctest:: Template String Literals (t-strings)
------------------------------------
>>> from fractions import Fraction An :dfn:`t-string` (formally a :dfn:`template string literal`) is
>>> f'{Fraction(1, 7):.6f}' a string literal that is prefixed with ``t`` or ``T``.
'0.142857'
>>> f'{Fraction(1, 7):_^+10}' These strings follow the same syntax and evaluation rules as
'___+1/7___' :ref:`formatted string literals <stdtypes-fstrings>`,
with for the following differences:
* Rather than evaluating to a ``str`` object, template string literals evaluate
to a :class:`string.templatelib.Template` object.
* The :func:`format` protocol is not used.
Instead, the format specifier and conversions (if any) are passed to
a new :class:`~string.templatelib.Interpolation` object that is created
for each evaluated expression.
It is up to code that processes the resulting :class:`~string.templatelib.Template`
object to decide how to handle format specifiers and conversions.
* Format specifiers containing nested replacement fields are evaluated eagerly,
prior to being passed to the :class:`~string.templatelib.Interpolation` object.
For instance, an interpolation of the form ``{amount:.{precision}f}`` will
evaluate the inner expression ``{precision}`` to determine the value of the
``format_spec`` attribute.
If ``precision`` were to be ``2``, the resulting format specifier
would be ``'.2f'``.
* When the equals sign ``'='`` is provided in an interpolation expression,
the text of the expression is appended to the literal string that precedes
the relevant interpolation.
This includes the equals sign and any surrounding whitespace.
The :class:`!Interpolation` instance for the expression will be created as
normal, except that :attr:`~string.templatelib.Interpolation.conversion` will
be set to '``r``' (:func:`repr`) by default.
If an explicit conversion or format specifier are provided,
this will override the default behaviour.
.. _old-string-formatting: .. _old-string-formatting:

View file

@ -174,7 +174,7 @@ Formally:
.. grammar-snippet:: .. grammar-snippet::
:group: python-grammar :group: python-grammar
strings: ( `STRING` | fstring)+ | tstring+ strings: ( `STRING` | `fstring`)+ | `tstring`+
This feature is defined at the syntactical level, so it only works with literals. This feature is defined at the syntactical level, so it only works with literals.
To concatenate string expressions at run time, the '+' operator may be used:: To concatenate string expressions at run time, the '+' operator may be used::

View file

@ -345,7 +345,15 @@ Whitespace between tokens
Except at the beginning of a logical line or in string literals, the whitespace Except at the beginning of a logical line or in string literals, the whitespace
characters space, tab and formfeed can be used interchangeably to separate characters space, tab and formfeed can be used interchangeably to separate
tokens. Whitespace is needed between two tokens only if their concatenation tokens:
.. grammar-snippet::
:group: python-grammar
whitespace: ' ' | tab | formfeed
Whitespace is needed between two tokens only if their concatenation
could otherwise be interpreted as a different token. For example, ``ab`` is one could otherwise be interpreted as a different token. For example, ``ab`` is one
token, but ``a b`` is two tokens. However, ``+a`` and ``+ a`` both produce token, but ``a b`` is two tokens. However, ``+a`` and ``+ a`` both produce
two tokens, ``+`` and ``a``, as ``+a`` is not a valid token. two tokens, ``+`` and ``a``, as ``+a`` is not a valid token.
@ -1032,124 +1040,59 @@ f-strings
--------- ---------
.. versionadded:: 3.6 .. versionadded:: 3.6
.. versionchanged:: 3.7
The :keyword:`await` and :keyword:`async for` can be used in expressions
within f-strings.
.. versionchanged:: 3.8
Added the debug specifier (``=``)
.. versionchanged:: 3.12
Many restrictions on expressions within f-strings have been removed.
Notably, nested strings, comments, and backslashes are now permitted.
A :dfn:`formatted string literal` or :dfn:`f-string` is a string literal A :dfn:`formatted string literal` or :dfn:`f-string` is a string literal
that is prefixed with '``f``' or '``F``'. These strings may contain that is prefixed with '``f``' or '``F``'.
replacement fields, which are expressions delimited by curly braces ``{}``. Unlike other string literals, f-strings do not have a constant value.
While other string literals always have a constant value, formatted strings They may contain *replacement fields* delimited by curly braces ``{}``.
are really expressions evaluated at run time. Replacement fields contain expressions which are evaluated at run time.
For example::
Escape sequences are decoded like in ordinary string literals (except when >>> who = 'nobody'
a literal is also marked as a raw string). After decoding, the grammar >>> nationality = 'Spanish'
for the contents of the string is: >>> f'{who.title()} expects the {nationality} Inquisition!'
'Nobody expects the Spanish Inquisition!'
.. productionlist:: python-grammar Any doubled curly braces (``{{`` or ``}}``) outside replacement fields
f_string: (`literal_char` | "{{" | "}}" | `replacement_field`)* are replaced with the corresponding single curly brace::
replacement_field: "{" `f_expression` ["="] ["!" `conversion`] [":" `format_spec`] "}"
f_expression: (`conditional_expression` | "*" `or_expr`)
: ("," `conditional_expression` | "," "*" `or_expr`)* [","]
: | `yield_expression`
conversion: "s" | "r" | "a"
format_spec: (`literal_char` | `replacement_field`)*
literal_char: <any code point except "{", "}" or NULL>
The parts of the string outside curly braces are treated literally, >>> print(f'{{...}}')
except that any doubled curly braces ``'{{'`` or ``'}}'`` are replaced {...}
with the corresponding single curly brace. A single opening curly
bracket ``'{'`` marks a replacement field, which starts with a Other characters outside replacement fields are treated like in ordinary
Python expression. To display both the expression text and its value after string literals.
evaluation, (useful in debugging), an equal sign ``'='`` may be added after the This means that escape sequences are decoded (except when a literal is
expression. A conversion field, introduced by an exclamation point ``'!'`` may also marked as a raw string), and newlines are possible in triple-quoted
follow. A format specifier may also be appended, introduced by a colon ``':'``. f-strings::
A replacement field ends with a closing curly bracket ``'}'``.
>>> name = 'Galahad'
>>> favorite_color = 'blue'
>>> print(f'{name}:\t{favorite_color}')
Galahad: blue
>>> print(rf"C:\Users\{name}")
C:\Users\Galahad
>>> print(f'''Three shall be the number of the counting
... and the number of the counting shall be three.''')
Three shall be the number of the counting
and the number of the counting shall be three.
Expressions in formatted string literals are treated like regular Expressions in formatted string literals are treated like regular
Python expressions surrounded by parentheses, with a few exceptions. Python expressions.
An empty expression is not allowed, and both :keyword:`lambda` and
assignment expressions ``:=`` must be surrounded by explicit parentheses.
Each expression is evaluated in the context where the formatted string literal Each expression is evaluated in the context where the formatted string literal
appears, in order from left to right. Replacement expressions can contain appears, in order from left to right.
newlines in both single-quoted and triple-quoted f-strings and they can contain An empty expression is not allowed, and both :keyword:`lambda` and
comments. Everything that comes after a ``#`` inside a replacement field assignment expressions ``:=`` must be surrounded by explicit parentheses::
is a comment (even closing braces and quotes). In that case, replacement fields
must be closed in a different line.
.. code-block:: text
>>> f"abc{a # This is a comment }"
... + 3}"
'abc5'
.. versionchanged:: 3.7
Prior to Python 3.7, an :keyword:`await` expression and comprehensions
containing an :keyword:`async for` clause were illegal in the expressions
in formatted string literals due to a problem with the implementation.
.. versionchanged:: 3.12
Prior to Python 3.12, comments were not allowed inside f-string replacement
fields.
When the equal sign ``'='`` is provided, the output will have the expression
text, the ``'='`` and the evaluated value. Spaces after the opening brace
``'{'``, within the expression and after the ``'='`` are all retained in the
output. By default, the ``'='`` causes the :func:`repr` of the expression to be
provided, unless there is a format specified. When a format is specified it
defaults to the :func:`str` of the expression unless a conversion ``'!r'`` is
declared.
.. versionadded:: 3.8
The equal sign ``'='``.
If a conversion is specified, the result of evaluating the expression
is converted before formatting. Conversion ``'!s'`` calls :func:`str` on
the result, ``'!r'`` calls :func:`repr`, and ``'!a'`` calls :func:`ascii`.
The result is then formatted using the :func:`format` protocol. The
format specifier is passed to the :meth:`~object.__format__` method of the
expression or conversion result. An empty string is passed when the
format specifier is omitted. The formatted result is then included in
the final value of the whole string.
Top-level format specifiers may include nested replacement fields. These nested
fields may include their own conversion fields and :ref:`format specifiers
<formatspec>`, but may not include more deeply nested replacement fields. The
:ref:`format specifier mini-language <formatspec>` is the same as that used by
the :meth:`str.format` method.
Formatted string literals may be concatenated, but replacement fields
cannot be split across literals.
Some examples of formatted string literals::
>>> name = "Fred"
>>> f"He said his name is {name!r}."
"He said his name is 'Fred'."
>>> f"He said his name is {repr(name)}." # repr() is equivalent to !r
"He said his name is 'Fred'."
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}" # nested fields
'result: 12.35'
>>> today = datetime(year=2017, month=1, day=27)
>>> f"{today:%B %d, %Y}" # using date format specifier
'January 27, 2017'
>>> f"{today=:%B %d, %Y}" # using date format specifier and debugging
'today=January 27, 2017'
>>> number = 1024
>>> f"{number:#0x}" # using integer format specifier
'0x400'
>>> foo = "bar"
>>> f"{ foo = }" # preserves whitespace
" foo = 'bar'"
>>> line = "The mill's closed"
>>> f"{line = }"
'line = "The mill\'s closed"'
>>> f"{line = :20}"
"line = The mill's closed "
>>> f"{line = !r:20}"
'line = "The mill\'s closed" '
>>> f'{(half := 1/2)}, {half * 42}'
'0.5, 21.0'
Reusing the outer f-string quoting type inside a replacement field is Reusing the outer f-string quoting type inside a replacement field is
permitted:: permitted::
@ -1158,10 +1101,6 @@ permitted::
>>> f"abc {a["x"]} def" >>> f"abc {a["x"]} def"
'abc 2 def' 'abc 2 def'
.. versionchanged:: 3.12
Prior to Python 3.12, reuse of the same quoting type of the outer f-string
inside a replacement field was not possible.
Backslashes are also allowed in replacement fields and are evaluated the same Backslashes are also allowed in replacement fields and are evaluated the same
way as in any other context:: way as in any other context::
@ -1172,23 +1111,84 @@ way as in any other context::
b b
c c
.. versionchanged:: 3.12 It is possible to nest f-strings::
Prior to Python 3.12, backslashes were not permitted inside an f-string
replacement field.
Formatted string literals cannot be used as docstrings, even if they do not >>> name = 'world'
include expressions. >>> f'Repeated:{f' hello {name}' * 3}'
'Repeated: hello world hello world hello world'
:: Portable Python programs should not use more than 5 levels of nesting.
.. impl-detail::
CPython does not limit nesting of f-strings.
Replacement expressions can contain newlines in both single-quoted and
triple-quoted f-strings and they can contain comments.
Everything that comes after a ``#`` inside a replacement field
is a comment (even closing braces and quotes).
This means that replacement fields with comments must be closed in a
different line:
.. code-block:: text
>>> a = 2
>>> f"abc{a # This comment }" continues until the end of the line
... + 3}"
'abc5'
After the expression, replacement fields may optionally contain:
* a *debug specifier* -- an equal sign (``=``), optionally surrounded by
whitespace on one or both sides;
* a *conversion specifier* -- ``!s``, ``!r`` or ``!a``; and/or
* a *format specifier* prefixed with a colon (``:``).
See the :ref:`Standard Library section on f-strings <stdtypes-fstrings>`
for details on how these fields are evaluated.
As that section explains, *format specifiers* are passed as the second argument
to the :func:`format` function to format a replacement field value.
For example, they can be used to specify a field width and padding characters
using the :ref:`Format Specification Mini-Language <formatspec>`::
>>> number = 14.3
>>> f'{number:20.7f}'
' 14.3000000'
Top-level format specifiers may include nested replacement fields::
>>> field_size = 20
>>> precision = 7
>>> f'{number:{field_size}.{precision}f}'
' 14.3000000'
These nested fields may include their own conversion fields and
:ref:`format specifiers <formatspec>`::
>>> number = 3
>>> f'{number:{field_size}}'
' 3'
>>> f'{number:{field_size:05}}'
'00000000000000000003'
However, these nested fields may not include more deeply nested replacement
fields.
Formatted string literals cannot be used as :term:`docstrings <docstring>`,
even if they do not include expressions::
>>> def foo(): >>> def foo():
... f"Not a docstring" ... f"Not a docstring"
... ...
>>> foo.__doc__ is None >>> print(foo.__doc__)
True None
See also :pep:`498` for the proposal that added formatted string literals, .. seealso::
and :meth:`str.format`, which uses a related format string mechanism.
* :pep:`498` -- Literal String Interpolation
* :pep:`701` -- Syntactic formalization of f-strings
* :meth:`str.format`, which uses a related format string mechanism.
.. _t-strings: .. _t-strings:
@ -1201,36 +1201,99 @@ t-strings
A :dfn:`template string literal` or :dfn:`t-string` is a string literal A :dfn:`template string literal` or :dfn:`t-string` is a string literal
that is prefixed with '``t``' or '``T``'. that is prefixed with '``t``' or '``T``'.
These strings follow the same syntax and evaluation rules as These strings follow the same syntax rules as
:ref:`formatted string literals <f-strings>`, with the following differences: :ref:`formatted string literals <f-strings>`.
For differences in evaluation rules, see the
:ref:`Standard Library section on t-strings <stdtypes-tstrings>`
* Rather than evaluating to a ``str`` object, template string literals evaluate
to a :class:`string.templatelib.Template` object.
* The :func:`format` protocol is not used. Formal grammar for f-strings
Instead, the format specifier and conversions (if any) are passed to ----------------------------
a new :class:`~string.templatelib.Interpolation` object that is created
for each evaluated expression.
It is up to code that processes the resulting :class:`~string.templatelib.Template`
object to decide how to handle format specifiers and conversions.
* Format specifiers containing nested replacement fields are evaluated eagerly, F-strings are handled partly by the :term:`lexical analyzer`, which produces the
prior to being passed to the :class:`~string.templatelib.Interpolation` object. tokens :py:data:`~token.FSTRING_START`, :py:data:`~token.FSTRING_MIDDLE`
For instance, an interpolation of the form ``{amount:.{precision}f}`` will and :py:data:`~token.FSTRING_END`, and partly by the parser, which handles
evaluate the inner expression ``{precision}`` to determine the value of the expressions in the replacement field.
``format_spec`` attribute. The exact way the work is split is a CPython implementation detail.
If ``precision`` were to be ``2``, the resulting format specifier
would be ``'.2f'``.
* When the equals sign ``'='`` is provided in an interpolation expression, Correspondingly, the f-string grammar is a mix of
the text of the expression is appended to the literal string that precedes :ref:`lexical and syntactic definitions <notation-lexical-vs-syntactic>`.
the relevant interpolation.
This includes the equals sign and any surrounding whitespace. Whitespace is significant in these situations:
The :class:`!Interpolation` instance for the expression will be created as
normal, except that :attr:`~string.templatelib.Interpolation.conversion` will * There may be no whitespace in :py:data:`~token.FSTRING_START` (between
be set to '``r``' (:func:`repr`) by default. the prefix and quote).
If an explicit conversion or format specifier are provided, * Whitespace in :py:data:`~token.FSTRING_MIDDLE` is part of the literal
this will override the default behaviour. string contents.
* In ``fstring_replacement_field``, if ``f_debug_specifier`` is present,
all whitespace after the opening brace until the ``f_debug_specifier``,
as well as whitespace immediatelly following ``f_debug_specifier``,
is retained as part of the expression.
.. impl-detail::
The expression is not handled in the tokenization phase; it is
retrieved from the source code using locations of the ``{`` token
and the token after ``=``.
The ``FSTRING_MIDDLE`` definition uses
:ref:`negative lookaheads <lexical-lookaheads>` (``!``)
to indicate special characters (backslash, newline, ``{``, ``}``) and
sequences (``f_quote``).
.. grammar-snippet::
:group: python-grammar
fstring: `FSTRING_START` `fstring_middle`* `FSTRING_END`
FSTRING_START: `fstringprefix` ("'" | '"' | "'''" | '"""')
FSTRING_END: `f_quote`
fstringprefix: <("f" | "fr" | "rf"), case-insensitive>
f_debug_specifier: '='
f_quote: <the quote character(s) used in FSTRING_START>
fstring_middle:
| `fstring_replacement_field`
| `FSTRING_MIDDLE`
FSTRING_MIDDLE:
| (!"\" !`newline` !'{' !'}' !`f_quote`) `source_character`
| `stringescapeseq`
| "{{"
| "}}"
| <newline, in triple-quoted f-strings only>
fstring_replacement_field:
| '{' `f_expression` [`f_debug_specifier`] [`fstring_conversion`]
[`fstring_full_format_spec`] '}'
fstring_conversion:
| "!" ("s" | "r" | "a")
fstring_full_format_spec:
| ':' `fstring_format_spec`*
fstring_format_spec:
| `FSTRING_MIDDLE`
| `fstring_replacement_field`
f_expression:
| ','.(`conditional_expression` | "*" `or_expr`)+ [","]
| `yield_expression`
.. note::
In the above grammar snippet, the ``f_quote`` and ``FSTRING_MIDDLE`` rules
are context-sensitive -- they depend on the contents of ``FSTRING_START``
of the nearest enclosing ``fstring``.
Constructing a more traditional formal grammar from this template is left
as an exercise for the reader.
The grammar for t-strings is identical to the one for f-strings, with *t*
instead of *f* at the beginning of rule and token names and in the prefix.
.. grammar-snippet::
:group: python-grammar
tstring: TSTRING_START tstring_middle* TSTRING_END
<rest of the t-string grammar is omitted; see above>
.. _numbers: .. _numbers: