annotationlib — Функции для интроспекции аннотаций
Добавлено в версии 3.14.
Исходный код: Lib/annotationlib.py
Модуль annotationlib предоставляет инструменты для интроспекции аннотаций модулей, классов и функций.
Аннотации вычисляются отложенно и часто содержат прямые ссылки на объекты, которые ещё не определены на момент создания аннотации. Этот модуль предоставляет набор низкоуровневых инструментов, с помощью которых можно надёжно получать аннотации, даже если они содержат прямые ссылки или другие сложные случаи.
Модуль поддерживает получение аннотаций в трёх основных форматах (см. Format), каждый из которых лучше подходит для разных случаев использования:
-
VALUEвычисляет аннотации и возвращает их значения. С этим форматом проще всего работать, но он может вызывать ошибки, например если аннотации содержат ссылки на неопределённые имена. -
FORWARDREFвозвращает объектыForwardRefдля аннотаций, которые не удаётся разрешить, что позволяет проверять аннотации, не вычисляя их. Этот формат полезен, когда нужно работать с аннотациями, которые могут содержать неразрешённые прямые ссылки. -
STRINGвозвращает аннотации в виде строки, похожей на ту, что была бы в исходном файле. Этот формат полезен генераторам документации, которым нужно показывать аннотации в удобочитаемом виде.
Функция get_annotations() — основная точка входа для получения аннотаций. Она принимает функцию, класс или модуль и возвращает словарь аннотаций в запрошенном формате. Этот модуль также предоставляет функции для непосредственной работы с функцией annotate, используемой для вычисления аннотаций, например get_annotate_from_class_namespace() и call_annotate_function(), а также функцию call_evaluate_function() для работы с функциями evaluate.
Предупреждение
Большинство функций этого модуля могут выполнять произвольный код; дополнительную информацию см. в разделе о безопасности.
См. также
PEP 649 предложил текущую модель работы аннотаций в Python.
PEP 749 развил различные аспекты PEP 649 и представил модуль annotationlib.
Рекомендации по работе с аннотациями содержит рекомендации по работе с аннотациями.
typing-extensions предоставляет обратный порт get_annotations(), который работает в более ранних версиях Python.
Семантика аннотаций
Способ вычисления аннотаций менялся на протяжении истории Python 3 и в настоящее время всё ещё зависит от импорта future. Использовались следующие модели выполнения аннотаций:
- Стандартная семантика (по умолчанию в Python 3.0–3.13; см. PEP 3107 и PEP 526): аннотации вычисляются сразу при встрече в исходном коде.
-
Строковые аннотации (используются с
from __future__ import annotationsв Python 3.7 и новее; см. PEP 563): аннотации сохраняются только в виде строк. - Отложенное вычисление (по умолчанию в Python 3.14 и новее; см. PEP 649 и PEP 749): аннотации вычисляются отложенно, только при обращении к ним.
Рассмотрим в качестве примера следующую программу:
def func(a: Cls) -> None:
print(a)
class Cls: pass
print(func.__annotations__)
Она будет работать следующим образом:
- При стандартной семантике (Python 3.13 и более ранние версии) на строке, где определена
func, возникнет ошибкаNameError, поскольку на тот моментCls— неопределённое имя. - При использовании строковых аннотаций (если используется
from __future__ import annotations) программа выведет{'a': 'Cls', 'return': 'None'}. - При отложенном вычислении (Python 3.14 и новее) программа выведет
{'a': <class 'Cls'>, 'return': None}.
Стандартная семантика использовалась с момента появления аннотаций функций в Python 3.0 (в соответствии с PEP 3107), поскольку это был самый простой и очевидный способ реализовать аннотации. Та же модель выполнения использовалась при добавлении аннотаций переменных в Python 3.6 (в соответствии с PEP 526). Однако стандартная семантика создавала проблемы при использовании аннотаций в качестве подсказок типов, например необходимость ссылаться на имена, которые ещё не определены в момент встречи аннотации. Кроме того, возникали проблемы с производительностью из-за выполнения аннотаций во время импорта модуля. Поэтому в Python 3.7 в PEP 563 была введена возможность сохранять аннотации в виде строк с помощью синтаксиса from __future__ import annotations. Тогда планировалось со временем сделать такое поведение стандартным, но обнаружилась проблема: интроспекция строковых аннотаций во время выполнения затруднена. Альтернативное предложение — PEP 649 — представило третью модель выполнения, отложенное вычисление, которая была реализована в Python 3.14. Строковые аннотации по-прежнему используются, если присутствует from __future__ import annotations, но со временем это поведение будет удалено.
Классы
-
class annotationlib.Format -
Перечисление
IntEnum, описывающее форматы, в которых могут возвращаться аннотации. Элементы этого перечисления или соответствующие им целочисленные значения можно передавать функцииget_annotations()и другим функциям этого модуля, а также функциям__annotate__.-
VALUE = 1 -
Значения — это результат вычисления выражений аннотаций.
-
VALUE_WITH_FAKE_GLOBALS = 2 -
Специальное значение, указывающее, что функция annotate вычисляется в особом окружении с фиктивными глобальными переменными. Получив это значение, функции annotate должны либо вернуть то же значение, что и для формата
Format.VALUE, либо вызвать исключениеNotImplementedError, показывая тем самым, что они не поддерживают выполнение в этом окружении. Этот формат используется только внутри модуля и не должен передаваться функциям этого модуля.
-
FORWARDREF = 3 -
Для определённых значений возвращаются реальные значения аннотаций (согласно формату
Format.VALUE), а для неопределённых — проксиForwardRef. Реальные объекты могут содержать ссылки на прокси-объектыForwardRef.
-
STRING = 4 -
Значениями являются текстовые строки аннотаций в том виде, в каком они представлены в исходном коде, с возможными изменениями, включая, помимо прочего, нормализацию пробелов и оптимизацию константных значений.
Точные значения этих строк могут измениться в будущих версиях Python.
Добавлено в версии 3.14.
-
-
class annotationlib.ForwardRef -
Прокси-объект для прямых ссылок в аннотациях.
Экземпляры этого класса возвращаются при использовании формата
FORWARDREF, если аннотации содержат имя, которое не удаётся разрешить. Это может произойти, когда в аннотации используется прямая ссылка, например если ссылка ведёт на класс, который ещё не определён.-
__forward_arg__ -
Строка, содержащая код, который был вычислен для создания
ForwardRef. Эта строка может не полностью совпадать с исходным кодом.
-
evaluate(*, owner=None, globals=None, locals=None, type_params=None, format=Format.VALUE) -
Вычисляет прямую ссылку и возвращает её значение.
Если аргумент format равен
VALUE(значение по умолчанию), этот метод может вызвать исключение, напримерNameError, если прямая ссылка указывает на имя, которое не удаётся разрешить. Аргументы этого метода можно использовать, чтобы задать привязки для имён, которые иначе оставались бы неопределёнными. Если аргумент format равенFORWARDREF, метод никогда не вызовет исключение, но может вернуть экземплярForwardRef. Например, если объект прямой ссылки содержит кодlist[undefined], гдеundefined— неопределённое имя, его вычисление с форматомFORWARDREFвернётlist[ForwardRef('undefined')]. Если аргумент format равенSTRING, метод вернёт__forward_arg__.Параметр owner — предпочтительный способ передать этому методу информацию об области видимости. Владелец объекта
ForwardRef— это объект, содержащий аннотацию, из которой происходитForwardRef, например объект модуля, типа или функции.Параметры globals, locals и type_params позволяют точнее управлять именами, доступными при вычислении
ForwardRef. Параметры globals и locals передаются функцииeval()и представляют глобальное и локальное пространства имён, в которых вычисляется имя. Параметр type_params важен для объектов, созданных с использованием встроенного синтаксиса для обобщённых классов и функций. Это кортеж параметров типа, находящихся в области видимости при вычислении прямой ссылки. Например, при вычисленииForwardRef, полученного из аннотации в пространстве имён класса обобщённого классаC, параметру type_params следует присвоить значениеC.__type_params__.Экземпляры
ForwardRef, возвращаемые функциейget_annotations(), сохраняют ссылки на информацию об области видимости, из которой они были получены, поэтому для вычисления таких объектов может быть достаточно вызвать этот метод без дополнительных аргументов. ЭкземплярыForwardRef, созданные другими способами, могут не содержать информации об области видимости, поэтому для успешного вычисления может потребоваться передать аргументы этому методу.Если не указаны параметры owner, globals, locals или type_params, а
ForwardRefне содержит информации о своём происхождении, используются пустые словари globals и locals.
Добавлено в версии 3.14.
-
Функции
-
annotationlib.annotations_to_string(annotations) -
Преобразует словарь аннотаций, содержащий значения времени выполнения, в словарь, содержащий только строки. Если значения уже не являются строками, они преобразуются с помощью
type_repr(). Эта функция предназначена для использования в качестве вспомогательной функции для пользовательских функций annotate, поддерживающих форматSTRING, но не имеющих доступа к коду, создающему аннотации.Например, она используется для реализации формата
STRINGдля классовtyping.TypedDict, созданных с помощью функционального синтаксиса:>>> from typing import TypedDict >>> Movie = TypedDict("movie", {"name": str, "year": int}) >>> get_annotations(Movie, format=Format.STRING) {'name': 'str', 'year': 'int'}Добавлено в версии 3.14.
-
annotationlib.call_annotate_function(annotate, format, *, owner=None) -
Вызывает функцию annotate annotate с заданным параметром format, являющимся членом перечисления
Format, и возвращает словарь аннотаций, созданный функцией.Эта вспомогательная функция необходима, поскольку функции annotate, создаваемые компилятором для функций, классов и модулей, при прямом вызове поддерживают только формат
VALUE. Для поддержки других форматов эта функция вызывает функцию annotate в специальном окружении, позволяющем ей создавать аннотации в других форматах. Это полезный строительный блок при реализации функциональности, которой требуется частично вычислять аннотации во время создания класса.owner — объект, которому принадлежит функция аннотаций; обычно это функция, класс или модуль. Если этот параметр задан, в формате
FORWARDREFон используется для создания объектаForwardRef, содержащего дополнительную информацию.См. также
PEP 649 содержит объяснение метода реализации, используемого этой функцией.
Добавлено в версии 3.14.
-
annotationlib.call_evaluate_function(evaluate, format, *, owner=None) -
Вызывает функцию evaluate evaluate с заданным параметром format, являющимся членом перечисления
Format, и возвращает значение, полученное от функции. Это похоже наcall_annotate_function(), однако последняя всегда возвращает словарь, сопоставляющий строки с аннотациями, а эта функция возвращает одно значение.Эта функция предназначена для использования с функциями evaluate, создаваемыми для лениво вычисляемых элементов, связанных с псевдонимами типов и параметрами типов:
-
typing.TypeAliasType.evaluate_value(), значение псевдонимов типов -
typing.TypeVar.evaluate_bound(), ограничение переменных типов -
typing.TypeVar.evaluate_constraints(), ограничения переменных типов -
typing.TypeVar.evaluate_default(), значение по умолчанию переменных типов -
typing.ParamSpec.evaluate_default(), значение по умолчанию спецификаций параметров -
typing.TypeVarTuple.evaluate_default(), значение по умолчанию кортежей переменных типов
owner — объект, которому принадлежит функция evaluate, например псевдоним типа или объект переменной типа.
Параметр format позволяет задать формат возвращаемого значения:
>>> 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'Добавлено в версии 3.14.
-
-
annotationlib.get_annotate_from_class_namespace(namespace) -
Извлекает функцию annotate из словаря пространства имён класса namespace. Возвращает
None, если словарь пространства имён не содержит функцию annotate. В первую очередь это полезно до полного создания класса (например, в метаклассе); после создания класса функцию annotate можно получить с помощьюcls.__annotate__. Пример использования этой функции в метаклассе см. ниже.Добавлено в версии 3.14.
-
annotationlib.get_annotations(obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE) -
Вычисляет словарь аннотаций объекта.
obj может быть вызываемым объектом, классом, модулем или другим объектом с атрибутами
__annotate__или__annotations__. Передача любого другого объекта вызывает исключениеTypeError.Параметр format определяет формат возвращаемых аннотаций и должен быть членом перечисления
Formatили его целочисленным эквивалентом. Различные форматы работают следующим образом:- VALUE: сначала проверяется
object.__annotations__; если он отсутствует, вызывается функцияobject.__annotate__, если она существует. -
FORWARDREF: если
object.__annotations__существует и успешно вычисляется, используется он; в противном случае вызывается функцияobject.__annotate__. Если она также отсутствует, выполняется повторная попытка получитьobject.__annotations__, и возникающая при этом ошибка повторно возбуждается.- При вызове
object.__annotate__сначала вызывается сFORWARDREF. Если этот формат не реализован, затем проверяется поддержкаVALUE_WITH_FAKE_GLOBALS, и он используется в окружении с фиктивными глобальными переменными. Если ни один из этих форматов не поддерживается, используется запасной вариант —VALUE. ЕслиVALUEзавершается ошибкой, будет возбуждено исключение, возникшее при этом вызове.
- При вызове
-
STRING: если
object.__annotate__существует, сначала вызывается он; в противном случае используетсяobject.__annotations__, а результат преобразуется в строку с помощьюannotations_to_string().- При вызове
object.__annotate__сначала вызывается сSTRING. Если этот формат не реализован, затем проверяется поддержкаVALUE_WITH_FAKE_GLOBALS, и он используется в окружении с фиктивными глобальными переменными. Если ни один из этих форматов не поддерживается, используется запасной вариант —VALUE, а результат преобразуется с помощьюannotations_to_string(). ЕслиVALUEзавершается ошибкой, будет возбуждено исключение, возникшее при этом вызове.
- При вызове
Возвращает словарь.
get_annotations()при каждом вызове возвращает новый словарь; два вызова для одного и того же объекта вернут два разных, но эквивалентных словаря.Эта функция берет на себя выполнение нескольких задач:
- Если значение eval_str равно true, значения типа
strпреобразуются из строк с помощьюeval(). Это предназначено для использования со строковыми аннотациями (from __future__ import annotations). Установка eval_str в true с форматами, отличными отFormat.VALUE, приводит к ошибке. - Если у obj нет словаря аннотаций, возвращает пустой словарь. (У функций и методов словарь аннотаций есть всегда; у классов, модулей и других типов вызываемых объектов его может не быть.)
- Игнорирует унаследованные аннотации классов, а также аннотации метаклассов. Если у класса нет собственного словаря аннотаций, возвращает пустой словарь.
- Для безопасности все обращения к членам объектов и значениям словарей выполняются с помощью
getattr()иdict.get().
Параметр eval_str определяет, заменяются ли значения типа
strрезультатом вызоваeval()для этих значений:- Если eval_str имеет значение true, для значений типа
strвызываетсяeval(). (Обратите внимание:get_annotations()не перехватывает исключения; еслиeval()вызывает исключение, оно развернет стек вызовов за пределы вызоваget_annotations().) - Если eval_str имеет значение false (по умолчанию), значения типа
strостаются без изменений.
Параметры globals и locals передаются в
eval(); дополнительную информацию см. в документации кeval(). Если globals или locals равенNone, эта функция может заменить это значение контекстно-зависимым значением по умолчанию в зависимости отtype(obj):- Если obj — модуль, для globals по умолчанию используется
obj.__dict__. - Если obj — класс, для globals по умолчанию используется
sys.modules[obj.__module__].__dict__, а для locals — пространство имён класса obj. - Если obj — вызываемый объект, для globals по умолчанию используется
obj.__globals__; однако если obj — обёрнутая функция (с использованиемfunctools.update_wrapper()) или объектfunctools.partial, выполняется снятие обёрток до обнаружения функции без обёртки.
Для доступа к словарю аннотаций любого объекта рекомендуется вызывать
get_annotations(). Дополнительную информацию о рекомендуемых способах работы с аннотациями см. в разделе Рекомендации по работе с аннотациями.>>> def f(a: int, b: str) -> float: ... pass >>> get_annotations(f) {'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'float'>}Добавлено в версии 3.14.
- VALUE: сначала проверяется
-
annotationlib.type_repr(value) -
Преобразует произвольное значение Python в формат, подходящий для использования в формате
STRING. Для большинства объектов вызываетсяrepr(), однако для некоторых объектов, например объектов типов, предусмотрена специальная обработка.Эта функция предназначена для использования в качестве вспомогательной функции для пользовательских функций annotate, поддерживающих формат
STRING, но не имеющих доступа к коду, создающему аннотации. Её также можно использовать для получения удобного для пользователя строкового представления других объектов, содержащих значения, часто встречающиеся в аннотациях.Добавлено в версии 3.14.
Примеры
Использование аннотаций в метаклассе
Метаклассу может потребоваться проверить или даже изменить аннотации в теле класса во время его создания. Для этого необходимо извлечь аннотации из словаря пространства имён класса. У классов, созданных с помощью from __future__ import annotations, аннотации находятся в ключе __annotations__ словаря. Для других классов с аннотациями можно использовать get_annotate_from_class_namespace(), чтобы получить функцию annotate, и call_annotate_function(), чтобы вызвать её и получить аннотации. Обычно лучше всего использовать формат FORWARDREF, поскольку он позволяет аннотациям ссылаться на имена, которые ещё нельзя разрешить при создании класса.
Чтобы изменить аннотации, рекомендуется создать функцию-обёртку annotate, которая вызывает исходную функцию annotate, вносит необходимые изменения и возвращает результат.
Ниже приведён пример метакласса, который отфильтровывает все аннотации typing.ClassVar класса и помещает их в отдельный атрибут:
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
Создание пользовательской вызываемой функции annotate
Пользовательские функции annotate могут быть обычными функциями, подобными тем, которые автоматически создаются для функций, классов и модулей. Также можно использовать инкапсуляцию, предоставляемую классами; в этом случае в качестве функции annotate можно использовать любой вызываемый объект.
Чтобы напрямую предоставлять форматы VALUE, STRING или FORWARDREF, функция annotate должна предоставлять следующий атрибут:
- Вызываемый объект
__call__с сигнатурой__call__(format, /) -> dict, который при вызове с поддерживаемым форматом не вызывает исключениеNotImplementedError.
Чтобы предоставлять формат VALUE_WITH_FAKE_GLOBALS, используемый для автоматической генерации STRING или FORWARDREF, если они не поддерживаются напрямую, функции annotate должны предоставлять следующие атрибуты:
- Вызываемый объект
__call__с сигнатурой__call__(format, /) -> dict, который при вызове сVALUE_WITH_FAKE_GLOBALSне вызывает исключениеNotImplementedError. - Объект кода
__code__, содержащий скомпилированный код функции annotate. - Необязательно: кортеж позиционных значений по умолчанию функции
__kwdefaults__, если функция, представленная объектом__code__, использует позиционные значения по умолчанию. - Необязательно: словарь значений по умолчанию для именованных аргументов функции
__defaults__, если функция, представленная объектом__code__, использует значения по умолчанию для именованных аргументов. - Необязательно: все остальные атрибуты функции.
class Annotate:
called_formats = []
def __call__(self, format=None, /, *, _self=None):
# When called with fake globals, `_self` will be the
# actual self value, and `self` will be the format.
if _self is not None:
self, format = _self, self
self.called_formats.append(format)
if format <= 2: # VALUE or VALUE_WITH_FAKE_GLOBALS
return {"x": MyType}
raise NotImplementedError
__code__ = __call__.__code__
__defaults__ = (None,)
__kwdefaults__ = property(lambda self: dict(_self=self))
__globals__ = {}
__builtins__ = {}
__closure__ = None
Затем её можно вызвать следующим образом:
>>> from annotationlib import call_annotate_function, Format
>>> call_annotate_function(Annotate(), format=Format.STRING)
{'x': 'MyType'}
Или использовать в качестве функции annotate объекта:
>>> from annotationlib import get_annotations, Format
>>> class C:
... pass
>>> C.__annotate__ = Annotate()
>>> get_annotations(Annotate(), format=Format.STRING)
{'x': 'MyType'}
Ограничения формата STRING
Формат STRING предназначен для приближённого восстановления исходного кода аннотации, однако используемый метод реализации означает, что точный исходный код удаётся восстановить не всегда.
Во-первых, преобразователь в строку, разумеется, не может восстановить сведения, отсутствующие в скомпилированном коде, включая комментарии, пробелы, расстановку скобок и операции, упрощённые компилятором.
Во-вторых, преобразователь в строку может перехватывать почти все операции с именами, поиск которых выполняется в некотором пространстве имён, но не может перехватывать операции, выполняемые исключительно над константами. Следовательно, запрашивать формат STRING для недоверенного кода также небезопасно: Python достаточно мощный язык, чтобы выполнять произвольный код даже без доступа к глобальным переменным или встроенным именам. Например:
>>> 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'}
Примечание
На момент написания этот конкретный пример работает, однако он зависит от деталей реализации и не гарантируется в будущих версиях.
Из различных видов выражений, существующих в Python и представленных модулем ast, одни поддерживаются, то есть формат STRING в целом может восстановить исходный код; другие не поддерживаются и могут привести к неверному результату или ошибке.
Поддерживаются следующие выражения (иногда с оговорками):
ast.BinOp-
-
ast.Invert(~),ast.UAdd(+) иast.USub(-) поддерживаются -
ast.Not(not) не поддерживается
-
-
ast.Dict(за исключением использования распаковки**) ast.Set-
ast.Call(за исключением использования распаковки**) -
ast.Constant(но не точное представление константы; например, экранирующие последовательности в строках теряются, а шестнадцатеричные числа преобразуются в десятичные) -
ast.Attribute(если значение не является константой) -
ast.Subscript(если значение не является константой) -
ast.Starred(распаковка*) ast.Nameast.Listast.Tupleast.Slice
Следующие выражения не поддерживаются, но при их обнаружении преобразователь в строку выдаёт информативную ошибку:
-
ast.FormattedValue(f-строки; ошибка не обнаруживается при использовании спецификаторов преобразования, например!r) -
ast.JoinedStr(f-строки)
Следующие выражения не поддерживаются и приводят к неверному результату:
Следующие выражения запрещены в областях видимости аннотаций и поэтому не рассматриваются:
Ограничения формата FORWARDREF
Формат FORWARDREF стремится по возможности возвращать реальные значения, заменяя всё, что не удаётся разрешить, объектами ForwardRef. На него распространяются в целом те же ограничения, что и на формат STRING: при вычислении аннотаций, выполняющих операции над литералами или использующих неподдерживаемые типы выражений, формат FORWARDREF может вызвать исключения.
Ниже приведено несколько примеров поведения с неподдерживаемыми выражениями:
>>> 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'}
Последствия интроспекции аннотаций для безопасности
Многие возможности этого модуля предполагают выполнение кода, связанного с аннотациями, который может делать что угодно. Например, get_annotations() может вызвать произвольную функцию annotate, а ForwardRef.evaluate() может вызвать eval() для произвольной строки. Код, содержащийся в аннотации, может выполнять произвольные системные вызовы, входить в бесконечный цикл или выполнять любые другие действия. Это также относится к любому обращению к атрибуту __annotations__ и к различным функциям модуля typing, работающим с аннотациями, например typing.get_type_hints().
Любая связанная с этим проблема безопасности также возникает сразу после импорта кода, который может содержать недоверенные аннотации: импорт кода всегда может привести к выполнению произвольных операций. Однако небезопасно принимать строки или другие данные из недоверенного источника и передавать их любому API для интроспекции аннотаций, например редактируя словарь __annotations__ или напрямую создавая объект ForwardRef.
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/annotationlib.html