PEP 649/749 : les annotations paresseuses

Python 3.14 change fondamentalement la façon dont les annotations de type sont traitées. Avec le PEP 649 (complété par le PEP 749), les annotations ne sont plus évaluées au moment de la définition de la classe ou de la fonction : elles sont stockées sous forme de descripteurs paresseux et ne sont résolues que lorsqu'on accède à __annotations__.

Différence avec PEP 563 (from __future__)

Le PEP 563 (from __future__ import annotations) transformait les annotations en chaînes de caractères. Le PEP 649 les conserve comme des expressions évaluables à la demande, ce qui préserve la compatibilité avec les bibliothèques de validation à l'exécution comme Pydantic ou attrs.

python
# Python 3.14 : les annotations sont évaluées paresseusement
# Plus besoin de from __future__ import annotations

class Noeud:
    """Référence en avant : fonctionne nativement."""
    valeur: int
    enfant: Noeud | None = None  # pas d'erreur NameError !

# L'annotation 'Noeud' n'est pas évaluée à la définition.
# Elle est résolue uniquement quand on y accède :
print(Noeud.__annotations__)  # {'valeur': <class 'int'>, 'enfant': ...}

# Avec get_annotations() pour un accès sûr :
import annotationlib
ann = annotationlib.get_annotations(Noeud, format=annotationlib.Format.FORWARDREF)
print(ann)  # résout les références en avant correctement

Impact sur la vérification de types

Les bibliothèques qui inspectent les annotations à l'exécution (Pydantic, dataclasses, FastAPI) bénéficient directement de ce changement : elles reçoivent des objets Python réels au lieu de chaînes à parser, tout en supportant les références en avant.

python
from dataclasses import dataclass


@dataclass
class Arbre:
    valeur: int
    gauche: Arbre | None = None
    droite: Arbre | None = None

    def profondeur(self) -> int:
        g = self.gauche.profondeur() if self.gauche else 0
        d = self.droite.profondeur() if self.droite else 0
        return 1 + max(g, d)


arbre = Arbre(
    1,
    gauche=Arbre(2, gauche=Arbre(4)),
    droite=Arbre(3),
)
print(arbre.profondeur())  # 3

# Les annotations sont des types réels, pas des chaînes :
import annotationlib
ann = annotationlib.get_annotations(Arbre)
print(ann['gauche'])  # Arbre | None

Sources