PEP 746 – Typüberprüfung von Annotated-Metadaten
- Autor:
- Adrian Garcia Badaracco <adrian at adriangb.com>
- Sponsor:
- Jelle Zijlstra <jelle.zijlstra at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Entwurf
- Typ:
- Standards Track
- Thema:
- Typisierung
- Erstellt:
- 20-Mai-2024
- Python-Version:
- 3.15
- Post-History:
- 20-Mai-2024
Zusammenfassung
Diese PEP schlägt einen Mechanismus zur Typüberprüfung von Metadaten vor, der den Typ typing.Annotated verwendet. Metadatenobjekte, die das neue Protokoll __supports_annotated_base__ implementieren, werden von statischen Typprüfern typüberprüft, um sicherzustellen, dass die Metadaten für den gegebenen Typ gültig sind.
Motivation
PEP 593 führte Annotated als Möglichkeit zur Anbringung von Laufzeitmetadaten an Typen ein. Im Allgemeinen sind die Metadaten nicht für statische Typprüfer bestimmt, aber selbst dann ist es oft nützlich, überprüfen zu können, ob die Metadaten für den gegebenen Typ sinnvoll sind.
Nehmen Sie das erste Beispiel in PEP 593, das Annotated verwendet, um Serialisierungsinformationen an ein Feld anzuhängen
class Student(struct2.Packed):
name: Annotated[str, struct2.ctype("<10s")]
Hier sind die struct2.ctype("<10s")-Metadaten für eine Serialisierungsbibliothek zur Serialisierung des Feldes bestimmt. Solche Bibliotheken können nur eine Teilmenge von Typen serialisieren: Es wäre beispielsweise nicht sinnvoll, Annotated[list[str], struct2.ctype("<10s")] zu schreiben. Dennoch bietet das Typsystem keine Möglichkeit, dies zu erzwingen. Die Metadaten werden von Typprüfern vollständig ignoriert.
Dieser Anwendungsfall tritt in Bibliotheken wie pydantic und msgspec auf, die Annotated verwenden, um Validierungs- und Konvertierungsinformationen an Felder anzuhängen, oder fastapi, das Annotated verwendet, um Parameter als aus Headern, Query-Strings oder Dependency Injection extrahiert zu kennzeichnen.
Spezifikation
Diese PEP führt ein Protokoll ein, das von statischen und Laufzeit-Typprüfern verwendet werden kann, um die Konsistenz zwischen Annotated-Metadaten und einem gegebenen Typ zu validieren. Objekte, die dieses Protokoll implementieren, haben ein Attribut namens __supports_annotated_base__, das angibt, ob die Metadaten für einen gegebenen Typ gültig sind
class Int64:
__supports_annotated_base__: int
Das Attribut kann auch als ClassVar markiert werden, um eine Interaktion mit Dataclasses zu vermeiden
from dataclasses import dataclass
from typing import ClassVar
@dataclass
class Gt:
value: int
__supports_annotated_base__: ClassVar[int]
Wenn ein statischer Typprüfer auf einen Typausdruck der Form Annotated[T, M1, M2, ...] trifft, sollte er sicherstellen, dass für jedes Metadatenelement in M1, M2, ... eines der folgenden gilt:
- Das Metadatenelement ergibt ein Objekt, das kein
__supports_annotated_base__-Attribut hat; oder - Das Metadatenelement ergibt ein Objekt
M, das ein__supports_annotated_base__-Attribut hat; undTist dem Typ vonM.__supports_annotated_base__zuweisbar.
Um generische Gt-Metadaten zu unterstützen, könnte man schreiben
from typing import Protocol
class SupportsGt[T](Protocol):
def __gt__(self, __other: T) -> bool:
...
class Gt[T]:
__supports_annotated_base__: ClassVar[SupportsGt[T]]
def __init__(self, value: T) -> None:
self.value = value
x1: Annotated[int, Gt(0)] = 1 # OK
x2: Annotated[str, Gt(0)] = 0 # type checker error: str is not assignable to SupportsGt[int]
x3: Annotated[int, Gt(1)] = 0 # OK for static type checkers; runtime type checkers may flag this
Abwärtskompatibilität
Metadaten, die das Protokoll nicht implementieren, werden für alle Typen als gültig betrachtet, sodass keine abwärtskompatiblen Änderungen für bestehenden Code eingeführt werden. Die neuen Prüfungen gelten nur für Metadatenobjekte, die das von dieser PEP spezifizierte Protokoll explizit implementieren.
Sicherheitsimplikationen
Keine.
Wie man das lehrt
Dieses Protokoll ist hauptsächlich für Bibliotheken gedacht, die Annotated-Metadaten bereitstellen; Endbenutzer dieser Bibliotheken müssen das Protokoll wahrscheinlich nicht selbst implementieren. Das Protokoll sollte in der Dokumentation von typing.Annotated und in der Typisierungspezifikation erwähnt werden.
Referenzimplementierung
Noch keine.
Abgelehnte Ideen
Einführung einer Typvariable anstelle einer generischen Klasse
Wir haben die Verwendung einer speziellen Typvariablen AnnotatedT = TypeVar("AnnotatedT") in Betracht gezogen, um den Typ T des inneren Typs in Annotated darzustellen; Metadaten würden gegen diese Typvariable typüberprüft werden. Dies würde jedoch die Verwendung der alten Typvariablensyntax (vor PEP 695) erfordern, was nun eine abgeratene Funktion ist. Darüber hinaus würde dies Typvariablen auf eine ungewöhnliche Weise verwenden, die nicht gut zum Rest des Typsystems passt.
Einführung eines neuen Typs in typing.py, von dem alle Metadatenobjekte erben sollten
Eine frühere Version dieser PEP schlug die Hinzufügung einer neuen generischen Basisklasse TypedMetadata[U] vor, von der Metadatenobjekte erben würden. Wenn ein Metadatenobjekt eine Unterklasse von TypedMetadata[U] ist, würden Typprüfer prüfen, ob der Basistyp der Annotation zu U zuweisbar ist. Dieser Mechanismus integriert sich jedoch nicht so gut in den Rest der Sprache; Python verwendet im Allgemeinen keine Markierungsbasisklassen. Darüber hinaus bietet er weniger Flexibilität als der aktuelle Vorschlag: Er würde keine Overloads zulassen und er würde von Metadatenobjekten verlangen, eine neue Basisklasse hinzuzufügen, was ihre Laufzeitimplementierung komplexer machen könnte.
Verwendung einer Methode anstelle eines Attributs für __supports_annotated_base__
Wir haben die Verwendung einer Methode anstelle eines Attributs für das Protokoll in Betracht gezogen, damit diese Methode zur Laufzeit verwendet werden kann, um die Gültigkeit der Metadaten zu überprüfen und Overloads oder die Rückgabe von booleschen Literalen zu unterstützen. Die Verwendung einer Methode fügt jedoch zusätzlichen Aufwand für die Implementierung hinzu, und der Wert der Laufzeitanwendungsfälle oder komplexerer Szenarien, die Overloads und die Rückgabe von booleschen Literalen umfassen, war nicht klar.
Danksagungen
Wir danken Eric Traut für den Vorschlag der Idee, ein Protokoll zu verwenden, und für die Implementierung vorläufiger Unterstützung in Pyright. Vielen Dank an Jelle Zijlstra für das Sponsoring dieser PEP.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0746.rst
Zuletzt geändert: 2025-05-06 20:54:28 GMT