python / python/cpython

Nested replacement fields switch f-string lexing out of format-spec mode

Ouverte
#157,491 1 commentaire 1 réaction 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

interpreter-core topic-parser type-bug
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

Description

Bug report

Summary

Closing a nested replacement field inside an f-string format specifier switches the lexer to middle-string mode even though the outer replacement field remains open. Braces that follow the nested field are then parsed as escaped literal content instead of expressions in the format specifier. The shared lexer path also affects t-strings and changes the diagnostic produced for a newline after a nested field.

Reproduction Code
class X:
    def __format__(self, spec):
        return repr(spec)


x = X()
y = "Y"
z = "Z"

print(f'{x:{{y}}}')
print(f'{x:{y}{{z}}}')
Actual Behavior
"{'Y'}"
'Y{z'}

The second f-string passes Y{z to X.__format__(). repr() returns 'Y{z', and the remaining } is appended as literal f-string content. The corresponding t-string, t'{x:{y}{{z}}}', records Y{z as its interpolation's format specifier and places the remaining } in the following literal string.

Expected Behavior

The second f-string should treat {{z}} as a nested replacement field whose expression is the set literal {z}. It should pass Y{'Z'} to X.__format__() and print:

"{'Y'}"
"Y{'Z'}"

The t-string interpolation should likewise record Y{'Z'} as its format specifier.

Root Cause

begin_ftstring_expr() increments replacement_depth when a replacement field starts. A nested replacement field within a format specifier therefore has a greater replacement depth than its enclosing field.

When the matching } is processed, _PyLexer_close_ftstring_expr() decrements replacement_depth and unconditionally selects FTSTRING_MODE_MIDDLE. After a nested field closes, the remaining non-zero replacement depth represents the still-open outer field, whose format specifier is still being scanned. Losing that state changes how _PyLexer_get_ftstring() handles subsequent braces and
newlines.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-157492

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Exécutez les deux exemples de f-string et comparez leur sortie avec le comportement attendu, y compris le cas t-string correspondant. Lisez ensuite begin_ftstring_expr() et _PyLexer_close_ftstring_expr(), que le rapport identifie comme les points d’entrée pertinents du lexer ; le travail est considéré comme terminé lorsque les champs imbriqués continuent d’être correctement gérés alors que le spécificateur de format externe est ouvert.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
compilers
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
35/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.