annotationlib — Funktionalität zur Introspektion von Annotationen

Hinzugefügt in Version 3.14.

Quellcode: Lib/annotationlib.py


Das Modul annotationlib stellt Werkzeuge zur Introspektion von Annotationen auf Modulen, Klassen und Funktionen bereit.

Annotationen werden verzögert ausgewertet und enthalten oft Vorwärtsreferenzen auf Objekte, die bei der Erstellung der Annotation noch nicht definiert sind. Dieses Modul stellt eine Reihe von Low-Level-Werkzeugen bereit, die verwendet werden können, um Annotationen auf zuverlässige Weise abzurufen, selbst im Beisein von Vorwärtsreferenzen und anderen Randfällen.

Dieses Modul unterstützt das Abrufen von Annotationen in drei Hauptformaten (siehe Format), von denen jedes für unterschiedliche Anwendungsfälle am besten geeignet ist

  • VALUE wertet die Annotationen aus und gibt deren Wert zurück. Dies ist am einfachsten zu handhaben, kann aber Fehler verursachen, z. B. wenn die Annotationen Verweise auf undefinierte Namen enthalten.

  • FORWARDREF gibt ForwardRef-Objekte für Annotationen zurück, die nicht aufgelöst werden können, was es Ihnen ermöglicht, die Annotationen zu inspizieren, ohne sie auszuwerten. Dies ist nützlich, wenn Sie mit Annotationen arbeiten müssen, die nicht aufgelöste Vorwärtsreferenzen enthalten können.

  • STRING gibt die Annotationen als Zeichenkette zurück, ähnlich wie sie in der Quelldatei erscheinen würden. Dies ist nützlich für Dokumentationsgeneratoren, die Annotationen auf lesbare Weise anzeigen möchten.

Die Funktion get_annotations() ist der Haupteinstiegspunkt für das Abrufen von Annotationen. Gegeben eine Funktion, Klasse oder ein Modul, gibt sie ein Annotations-Dictionary im angeforderten Format zurück. Dieses Modul stellt auch Funktionalität für die direkte Arbeit mit der annotate function bereit, die zur Auswertung von Annotationen verwendet wird, wie z. B. get_annotate_from_class_namespace() und call_annotate_function(), sowie die Funktion call_evaluate_function() für die Arbeit mit evaluate functions.

Vorsicht

Die meisten Funktionen in diesem Modul können beliebigen Code ausführen. Weitere Informationen finden Sie im Abschnitt Sicherheit.

Siehe auch

PEP 649 schlug das aktuelle Modell für die Funktionsweise von Annotationen in Python vor.

PEP 749 erweiterte verschiedene Aspekte von PEP 649 und führte das Modul annotationlib ein.

Annotations Best Practices bietet Best Practices für die Arbeit mit Annotationen.

typing-extensions bietet einen Backport von get_annotations(), der auf früheren Python-Versionen funktioniert.

Annotation Semantics

Die Art und Weise, wie Annotationen ausgewertet werden, hat sich im Laufe der Geschichte von Python 3 geändert und hängt derzeit noch von einem future import ab. Es gab Ausführungsmodelle für Annotationen

  • Stock Semantics (Standard in Python 3.0 bis 3.13; siehe PEP 3107 und PEP 526): Annotationen werden eifrig ausgewertet, sobald sie im Quellcode angetroffen werden.

  • Stringified Annotations (verwendet mit from __future__ import annotations in Python 3.7 und neuer; siehe PEP 563): Annotationen werden nur als Strings gespeichert.

  • Deferred Evaluation (Standard in Python 3.14 und neuer; siehe PEP 649 und PEP 749): Annotationen werden verzögert ausgewertet, erst wenn sie aufgerufen werden.

Als Beispiel betrachten Sie das folgende Programm

def func(a: Cls) -> None:
    print(a)

class Cls: pass

print(func.__annotations__)

Dies wird wie folgt verarbeitet

  • Unter Stock Semantics (Python 3.13 und früher) wird ein NameError in der Zeile ausgelöst, in der func definiert ist, da Cls zu diesem Zeitpunkt ein undefinierter Name ist.

  • Unter stringifizierten Annotationen (wenn from __future__ import annotations verwendet wird) wird {'a': 'Cls', 'return': 'None'} ausgegeben.

  • Unter verzögerter Auswertung (Python 3.14 und später) wird {'a': <class 'Cls'>, 'return': None} ausgegeben.

Stock Semantics wurden verwendet, als Funktionsannotationen erstmals in Python 3.0 (durch PEP 3107) eingeführt wurden, da dies der einfachste und offensichtlichste Weg zur Implementierung von Annotationen war. Das gleiche Ausführungsmodell wurde verwendet, als Variablennamenannotationen in Python 3.6 (durch PEP 526) eingeführt wurden. Stock Semantics verursachten jedoch Probleme bei der Verwendung von Annotationen als Typ-Hints, wie z. B. die Notwendigkeit, auf Namen zu verweisen, die noch nicht definiert waren, als die Annotation angetroffen wurde. Zusätzlich gab es Leistungsprobleme bei der Auswertung von Annotationen zur Modulimportzeit. Daher wurde in Python 3.7 PEP 563 die Möglichkeit eingeführt, Annotationen mithilfe der Syntax from __future__ import annotations als Strings zu speichern. Der Plan war damals, dieses Verhalten schließlich zum Standard zu machen, aber ein Problem trat auf: stringifizierte Annotationen sind schwieriger zu verarbeiten für diejenigen, die Annotationen zur Laufzeit introspektieren. Ein alternativer Vorschlag, PEP 649, führte das dritte Ausführungsmodell, die verzögerte Auswertung, ein und wurde in Python 3.14 implementiert. Stringifizierte Annotationen werden weiterhin verwendet, wenn from __future__ import annotations vorhanden ist, aber dieses Verhalten wird schließlich entfernt werden.

Klassen

class annotationlib.Format

Ein IntEnum, das die Formate beschreibt, in denen Annotationen zurückgegeben werden können. Mitglieder der Enum oder ihre entsprechenden ganzzahligen Werte können an get_annotations() und andere Funktionen in diesem Modul sowie an __annotate__-Funktionen übergeben werden.

VALUE = 1

Werte sind das Ergebnis der Auswertung der Annotationsausdrücke.

VALUE_WITH_FAKE_GLOBALS = 2

Spezieller Wert, der verwendet wird, um zu signalisieren, dass eine Annotate-Funktion in einer speziellen Umgebung mit gefälschten Globals ausgewertet wird. Wenn dieser Wert übergeben wird, sollten Annotate-Funktionen entweder denselben Wert wie für das Format Format.VALUE zurückgeben oder NotImplementedError auslösen, um zu signalisieren, dass sie die Ausführung in dieser Umgebung nicht unterstützen. Dieses Format wird nur intern verwendet und sollte nicht an die Funktionen in diesem Modul übergeben werden.

FORWARDREF = 3

Werte sind reale Annotationswerte (gemäß dem Format Format.VALUE) für definierte Werte und ForwardRef-Proxys für undefinierte Werte. Reale Objekte können Verweise auf ForwardRef-Proxy-Objekte enthalten.

STRING = 4

Werte sind der Text der Annotation, wie er im Quellcode erscheint, bis hin zu Modifikationen, die Whitespace-Normalisierungen und Optimierungen von konstanten Werten einschließen, aber nicht darauf beschränkt sind.

Die exakten Werte dieser Strings können sich in zukünftigen Versionen von Python ändern.

Hinzugefügt in Version 3.14.

class annotationlib.ForwardRef

Ein Proxy-Objekt für Vorwärtsreferenzen in Annotationen.

Instanzen dieser Klasse werden zurückgegeben, wenn das Format FORWARDREF verwendet wird und Annotationen einen Namen enthalten, der nicht aufgelöst werden kann. Dies kann passieren, wenn eine Vorwärtsreferenz in einer Annotation verwendet wird, z. B. wenn auf eine Klasse verwiesen wird, bevor sie definiert ist.

__forward_arg__

Eine Zeichenkette, die den Code enthält, der ausgewertet wurde, um die ForwardRef zu erzeugen. Die Zeichenkette ist möglicherweise nicht exakt äquivalent zum ursprünglichen Quelltext.

evaluate(*, owner=None, globals=None, locals=None, type_params=None, format=Format.VALUE)

Wertet die Vorwärtsreferenz aus und gibt ihren Wert zurück.

Wenn das Argument format VALUE (Standard) ist, kann diese Methode eine Ausnahme auslösen, wie z. B. NameError, wenn die Vorwärtsreferenz auf einen Namen verweist, der nicht aufgelöst werden kann. Die Argumente für diese Methode können verwendet werden, um Bindungen für Namen bereitzustellen, die andernfalls undefiniert wären. Wenn das Argument format FORWARDREF ist, löst die Methode nie eine Ausnahme aus, kann aber eine ForwardRef-Instanz zurückgeben. Wenn die Vorwärtsreferenzobjekt beispielsweise den Code list[undefined] enthält, wobei undefined ein nicht definierter Name ist, wird die Auswertung mit dem Format FORWARDREF zu list[ForwardRef('undefined')] führen. Wenn das Argument format STRING ist, gibt die Methode __forward_arg__ zurück.

Das Argument owner bietet den bevorzugten Mechanismus zur Übergabe von Bereichsinformationen an diese Methode. Der Besitzer einer ForwardRef ist das Objekt, das die Anmerkung enthält, von der die ForwardRef abgeleitet ist, z. B. ein Modulobjekt, ein Typobjekt oder ein Funktions-Objekt.

Die Argumente globals, locals und type_params bieten einen präziseren Mechanismus zur Steuerung der Namen, die verfügbar sind, wenn die ForwardRef ausgewertet wird. globals und locals werden an eval() übergeben und stellen die globalen und lokalen Namensräume dar, in denen der Name ausgewertet wird. Das Argument type_params ist relevant für Objekte, die mit der nativen Syntax für generische Klassen und Funktionen erstellt wurden. Es ist ein Tupel von Typ-Parametern, die während der Auswertung der Vorwärtsreferenz im Geltungsbereich liegen. Wenn beispielsweise eine ForwardRef ausgewertet wird, die aus einer Annotation im Klassen-Namensraum einer generischen Klasse C stammt, sollte type_params auf C.__type_params__ gesetzt werden.

ForwardRef-Instanzen, die von get_annotations() zurückgegeben werden, behalten Verweise auf Informationen über den Bereich, aus dem sie stammen, bei. Das Aufrufen dieser Methode ohne weitere Argumente kann daher ausreichen, um solche Objekte auszuwerten. ForwardRef-Instanzen, die auf andere Weise erstellt wurden, haben möglicherweise keine Informationen über ihren Bereich, sodass die Übergabe von Argumenten an diese Methode erforderlich sein kann, um sie erfolgreich auszuwerten.

Wenn keine owner, globals, locals oder type_params angegeben sind und die ForwardRef keine Informationen über ihren Ursprung enthält, werden leere Globals- und Locals-Dictionaries verwendet.

Hinzugefügt in Version 3.14.

Funktionen

annotationlib.annotations_to_string(annotations)

Konvertiert ein Annotations-Dictionary, das Laufzeitwerte enthält, in ein Dictionary, das nur Strings enthält. Wenn die Werte keine Strings sind, werden sie mithilfe von type_repr() konvertiert. Dies dient als Hilfsmittel für benutzerdefinierte Annotate-Funktionen, die das Format STRING unterstützen, aber keinen Zugriff auf den Code haben, der die Annotationen erstellt.

Dies wird beispielsweise zur Implementierung von STRING für typing.TypedDict-Klassen verwendet, die über die funktionale Syntax erstellt wurden

>>> from typing import TypedDict
>>> Movie = TypedDict("movie", {"name": str, "year": int})
>>> get_annotations(Movie, format=Format.STRING)
{'name': 'str', 'year': 'int'}

Hinzugefügt in Version 3.14.

annotationlib.call_annotate_function(annotate, format, *, owner=None)

Ruft die annotate function annotate mit dem angegebenen format, einem Mitglied der Format-Enum, auf und gibt das von der Funktion erzeugte Annotations-Dictionary zurück.

Diese Hilfsfunktion ist erforderlich, da von den Compilern für Funktionen, Klassen und Module generierte Annotate-Funktionen beim direkten Aufruf nur das Format VALUE unterstützen. Um andere Formate zu unterstützen, ruft diese Funktion die Annotate-Funktion in einer speziellen Umgebung auf, die es ihr ermöglicht, Annotationen in anderen Formaten zu erzeugen. Dies ist ein nützlicher Baustein bei der Implementierung von Funktionalität, die Annotationen teilweise auswerten muss, während eine Klasse konstruiert wird.

owner ist das Objekt, dem die Annotate-Funktion gehört, normalerweise eine Funktion, Klasse oder ein Modul. Wenn angegeben, wird es im Format FORWARDREF verwendet, um ein ForwardRef-Objekt zu erzeugen, das mehr Informationen trägt.

Siehe auch

PEP 649 enthält eine Erklärung der von dieser Funktion verwendeten Implementierungstechnik.

Hinzugefügt in Version 3.14.

annotationlib.call_evaluate_function(evaluate, format, *, owner=None)

Ruft die evaluate function evaluate mit dem angegebenen format, einem Mitglied der Format-Enum, auf und gibt den von der Funktion erzeugten Wert zurück. Dies ähnelt call_annotate_function(), aber letztere gibt immer ein Dictionary zurück, das Strings auf Annotationen abbildet, während diese Funktion einen einzelnen Wert zurückgibt.

Dies ist für die Verwendung mit den von den Compilern generierten Auswertungsfunktionen für verzögert ausgewertete Elemente im Zusammenhang mit Typ-Aliassen und Typ-Parametern vorgesehen.

owner ist das Objekt, dem die Auswertungsfunktion gehört, wie z. B. das Typ-Alias- oder Tyvariablen-Objekt.

format kann verwendet werden, um das Format zu steuern, in dem der Wert zurückgegeben wird

>>> type Alias = undefined
>>> call_evaluate_function(Alias.evaluate_value, Format.VALUE)
Traceback (most recent call last):
...
NameError: name 'undefined' is not defined
>>> call_evaluate_function(Alias.evaluate_value, Format.FORWARDREF)
ForwardRef('undefined')
>>> call_evaluate_function(Alias.evaluate_value, Format.STRING)
'undefined'

Hinzugefügt in Version 3.14.

annotationlib.get_annotate_from_class_namespace(namespace)

Ruft die annotate function aus einem Klassen-Namensraum-Dictionary namespace ab. Gibt None zurück, wenn der Namensraum keine Annotate-Funktion enthält. Dies ist hauptsächlich vor der vollständigen Erstellung der Klasse (z. B. in einer Metaklasse) nützlich; nachdem die Klasse existiert, kann die Annotate-Funktion mit cls.__annotate__ abgerufen werden. Siehe unten für ein Beispiel, das diese Funktion in einer Metaklasse verwendet.

Hinzugefügt in Version 3.14.

annotationlib.get_annotations(obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE)

Berechnet das Annotations-Dictionary für ein Objekt.

obj kann ein aufrufbares Objekt, eine Klasse, ein Modul oder ein anderes Objekt mit den Attributen __annotate__ oder __annotations__ sein. Das Übergeben eines anderen Objekts löst einen TypeError aus.

Der Parameter format steuert das Format, in dem Annotationen zurückgegeben werden, und muss ein Mitglied der Format-Enum oder dessen ganzzahlige Entsprechung sein. Die verschiedenen Formate funktionieren wie folgt:

  • VALUE: Zuerst wird versucht, object.__annotations__ abzurufen; wenn dies nicht existiert, wird die Funktion object.__annotate__ aufgerufen, falls sie existiert.

  • FORWARDREF: Wenn object.__annotations__ existiert und erfolgreich ausgewertet werden kann, wird es verwendet; andernfalls wird die Funktion object.__annotate__ aufgerufen. Wenn auch diese nicht existiert, wird erneut versucht, object.__annotations__ abzurufen, und jeder Fehler beim Zugriff darauf wird erneut ausgelöst.

  • STRING: Wenn object.__annotate__ existiert, wird es zuerst aufgerufen; andernfalls wird object.__annotations__ verwendet und mit annotations_to_string() in eine Zeichenkette umgewandelt.

Gibt ein Wörterbuch zurück. get_annotations() gibt bei jedem Aufruf ein neues Wörterbuch zurück; ein zweifacher Aufruf auf dasselbe Objekt liefert zwei verschiedene, aber äquivalente Wörterbücher.

Diese Funktion kümmert sich um mehrere Details für Sie:

  • Wenn eval_str wahr ist, werden Werte vom Typ str mit eval() aus ihrer Zeichenkettenform gelöst. Dies ist für die Verwendung mit Zeichenkettenannotationen gedacht (from __future__ import annotations). Es ist ein Fehler, eval_str auf wahr zu setzen, wenn andere Formate als Format.VALUE verwendet werden.

  • Wenn obj keinen Annotations-Wörterbuch hat, wird ein leeres Wörterbuch zurückgegeben. (Funktionen und Methoden haben immer ein Annotations-Wörterbuch; Klassen, Module und andere Arten von aufrufbaren Objekten möglicherweise nicht.)

  • Ignoriert geerbte Annotationen von Klassen sowie Annotationen von Metaklassen. Wenn eine Klasse kein eigenes Annotations-Wörterbuch hat, wird ein leeres Wörterbuch zurückgegeben.

  • Alle Zugriffe auf Objektmember und Wörterbuchwerte erfolgen zur Sicherheit über getattr() und dict.get().

eval_str steuert, ob Werte vom Typ str durch das Ergebnis des Aufrufs von eval() auf diese Werte ersetzt werden oder nicht.

  • Wenn eval_str wahr ist, wird eval() auf Werte vom Typ str aufgerufen. (Beachten Sie, dass get_annotations() keine Ausnahmen abfängt; wenn eval() eine Ausnahme auslöst, wird der Stack-Aufruf über den get_annotations()-Aufruf hinaus zurückgesetzt.)

  • Wenn eval_str falsch (der Standardwert) ist, bleiben Werte vom Typ str unverändert.

globals und locals werden an eval() übergeben; siehe die Dokumentation für eval() für weitere Informationen. Wenn globals oder locals None ist, kann diese Funktion diesen Wert durch einen kontextspezifischen Standardwert ersetzen, abhängig von type(obj).

  • Wenn obj ein Modul ist, ist globals standardmäßig obj.__dict__.

  • Wenn obj eine Klasse ist, ist globals standardmäßig sys.modules[obj.__module__].__dict__ und locals ist standardmäßig der Namensraum der Klasse obj.

  • Wenn obj ein aufrufbares Objekt ist, ist globals standardmäßig obj.__globals__. Wenn obj jedoch eine dekorierte Funktion (unter Verwendung von functools.update_wrapper()) oder ein functools.partial-Objekt ist, wird es entpackt, bis eine nicht dekorierte Funktion gefunden wird.

Der Aufruf von get_annotations() ist die empfohlene Methode zum Zugriff auf das Annotations-Wörterbuch eines beliebigen Objekts. Weitere Informationen zu Best Practices für Annotationen finden Sie unter Best Practices für Annotationen.

>>> def f(a: int, b: str) -> float:
...     pass
>>> get_annotations(f)
{'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'float'>}

Hinzugefügt in Version 3.14.

annotationlib.type_repr(value)

Konvertiert einen beliebigen Python-Wert in ein Format, das für die Verwendung durch das STRING-Format geeignet ist. Dies ruft repr() für die meisten Objekte auf, hat aber spezielle Handhabung für einige Objekte, wie z. B. Typobjekte.

Dies ist als Hilfsmittel für benutzerdefinierte Annotationsfunktionen gedacht, die das STRING-Format unterstützen, aber keinen Zugriff auf den Code haben, der die Annotationen erstellt. Es kann auch verwendet werden, um eine benutzerfreundliche Zeichenkettendarstellung für andere Objekte bereitzustellen, die Werte enthalten, die häufig in Annotationen vorkommen.

Hinzugefügt in Version 3.14.

Rezepte

Verwendung von Annotationen in einer Metaklasse

Eine Metaklasse möchte möglicherweise die Annotationen im Klassenrumpf während der Klassenerstellung untersuchen oder sogar modifizieren. Dies erfordert den Abruf von Annotationen aus dem Wörterbuch des Klassen-Namensraums. Für Klassen, die mit from __future__ import annotations erstellt wurden, befinden sich die Annotationen unter dem Schlüssel __annotations__ des Wörterbuchs. Für andere Klassen mit Annotationen kann get_annotate_from_class_namespace() verwendet werden, um die Annotationsfunktion zu erhalten, und call_annotate_function() kann verwendet werden, um sie aufzurufen und die Annotationen abzurufen. Die Verwendung des FORWARDREF-Formats ist normalerweise am besten, da dies ermöglicht, dass sich Annotationen auf Namen beziehen, die zum Zeitpunkt der Klassenerstellung noch nicht aufgelöst werden können.

Um die Annotationen zu ändern, ist es am besten, eine Wrapper-Annotationsfunktion zu erstellen, die die ursprüngliche Annotationsfunktion aufruft, notwendige Anpassungen vornimmt und das Ergebnis zurückgibt.

Unten finden Sie ein Beispiel für eine Metaklasse, die alle typing.ClassVar-Annotationen aus der Klasse herausfiltert und sie in einem separaten Attribut speichert.

import annotationlib
import typing

class ClassVarSeparator(type):
   def __new__(mcls, name, bases, ns):
      if "__annotations__" in ns:  # from __future__ import annotations
         annotations = ns["__annotations__"]
         classvar_keys = {
            key for key, value in annotations.items()
            # Use string comparison for simplicity; a more robust solution
            # could use annotationlib.ForwardRef.evaluate
            if value.startswith("ClassVar")
         }
         classvars = {key: annotations[key] for key in classvar_keys}
         ns["__annotations__"] = {
            key: value for key, value in annotations.items()
            if key not in classvar_keys
         }
         wrapped_annotate = None
      elif annotate := annotationlib.get_annotate_from_class_namespace(ns):
         annotations = annotationlib.call_annotate_function(
            annotate, format=annotationlib.Format.FORWARDREF
         )
         classvar_keys = {
            key for key, value in annotations.items()
            if typing.get_origin(value) is typing.ClassVar
         }
         classvars = {key: annotations[key] for key in classvar_keys}

         def wrapped_annotate(format):
            annos = annotationlib.call_annotate_function(annotate, format, owner=typ)
            return {key: value for key, value in annos.items() if key not in classvar_keys}

      else:  # no annotations
         classvars = {}
         wrapped_annotate = None
      typ = super().__new__(mcls, name, bases, ns)

      if wrapped_annotate is not None:
         # Wrap the original __annotate__ with a wrapper that removes ClassVars
         typ.__annotate__ = wrapped_annotate
      typ.classvars = classvars  # Store the ClassVars in a separate attribute
      return typ

Einschränkungen des STRING-Formats

Das STRING-Format zielt darauf ab, den Quellcode der Annotation zu approximieren, aber die verwendete Implementierungsstrategie bedeutet, dass es nicht immer möglich ist, den exakten Quellcode wiederherzustellen.

Erstens kann der Stringifier natürlich keine Informationen wiederherstellen, die nicht im kompilierten Code vorhanden sind, einschließlich Kommentare, Leerzeichen, Klammerung und Operationen, die vom Compiler vereinfacht werden.

Zweitens kann der Stringifier fast alle Operationen abfangen, die sich auf Namen beziehen, die in einem bestimmten Geltungsbereich nachgeschlagen werden, aber er kann keine Operationen abfangen, die vollständig auf Konstanten operieren. Daraus folgt, dass es auch nicht sicher ist, das STRING-Format für nicht vertrauenswürdigen Code anzufordern: Python ist mächtig genug, um willkürliche Codeausführung zu erreichen, selbst ohne Zugriff auf Globals oder Builtins. Zum Beispiel:

>>> def f(x: (1).__class__.__base__.__subclasses__()[-1].__init__.__builtins__["print"]("Hello world")): pass
...
>>> annotationlib.get_annotations(f, format=annotationlib.Format.STRING)
Hello world
{'x': 'None'}

Hinweis

Dieses spezielle Beispiel funktioniert zum Zeitpunkt des Schreibens, aber es basiert auf Implementierungsdetails und funktioniert möglicherweise in Zukunft nicht mehr.

Von den verschiedenen Arten von Ausdrücken in Python, wie sie vom ast-Modul dargestellt werden, werden einige Ausdrücke unterstützt, was bedeutet, dass das STRING-Format im Allgemeinen den ursprünglichen Quellcode wiederherstellen kann; andere werden nicht unterstützt, was bedeutet, dass sie zu falschen Ausgaben oder Fehlern führen können.

Die folgenden werden unterstützt (manchmal mit Einschränkungen):

Die folgenden werden nicht unterstützt, lösen aber beim Auftreten durch den Stringifier eine informative Fehlermeldung aus:

Die folgenden werden nicht unterstützt und führen zu fehlerhaften Ausgaben:

Die folgenden sind in Annotations-Geltungsbereichen nicht zulässig und daher nicht relevant:

Einschränkungen des FORWARDREF-Formats

Das FORWARDREF-Format zielt darauf ab, so weit wie möglich echte Werte zu erzeugen, wobei alles, was nicht aufgelöst werden kann, durch ForwardRef-Objekte ersetzt wird. Es unterliegt im Wesentlichen denselben Einschränkungen wie das STRING-Format: Annotationen, die Operationen auf Literalen durchführen oder nicht unterstützte Ausdruckstypen verwenden, können Ausnahmen auslösen, wenn sie mit dem FORWARDREF-Format ausgewertet werden.

Unten sind einige Beispiele für das Verhalten mit nicht unterstützten Ausdrücken aufgeführt.

>>> from annotationlib import get_annotations, Format
>>> def zerodiv(x: 1 / 0): ...
>>> get_annotations(zerodiv, format=Format.STRING)
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero
>>> get_annotations(zerodiv, format=Format.FORWARDREF)
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero
>>> def ifexp(x: 1 if y else 0): ...
>>> get_annotations(ifexp, format=Format.STRING)
{'x': '1'}

Sicherheitsimplikationen der Introspektion von Annotationen

Ein Großteil der Funktionalität in diesem Modul beinhaltet die Ausführung von Code im Zusammenhang mit Annotationen, der dann beliebige Dinge tun kann. Zum Beispiel kann get_annotations() eine beliebige Annotationsfunktion aufrufen, und ForwardRef.evaluate() kann eval() auf einer beliebigen Zeichenkette aufrufen. Code, der in einer Annotation enthalten ist, kann willkürliche Systemaufrufe tätigen, eine Endlosschleife betreten oder eine beliebige andere Operation durchführen. Dies gilt auch für jeden Zugriff auf das Attribut __annotations__ und für verschiedene Funktionen im typing-Modul, die mit Annotationen arbeiten, wie z. B. typing.get_type_hints().

Jedes Sicherheitsproblem, das sich daraus ergibt, gilt auch unmittelbar nach dem Importieren von Code, der nicht vertrauenswürdige Annotationen enthalten kann: Das Importieren von Code kann immer beliebige Operationen auslösen. Es ist jedoch unsicher, Zeichenketten oder andere Eingaben von einer nicht vertrauenswürdigen Quelle zu akzeptieren und sie an eine der APIs zur Introspektion von Annotationen zu übergeben, z. B. durch Bearbeiten eines __annotations__-Wörterbuchs oder durch direktes Erstellen eines ForwardRef-Objekts.