Содержание страницы
annotationlib – Функциональность для интроспекции аннотаций¶annotationlib – Functionality for introspecting annotations
Добавлено в версии 3.14.
Исходный код: Lib/annotationlib.py
Модуль annotationlib предоставляет инструменты для интроспекции
аннотаций в модулях, классах и функциях.
Аннотации вычисляются лениво и часто содержат прямые ссылки на объекты, которые ещё не определены на момент создания аннотации. Этот модуль предоставляет набор низкоуровневых инструментов, которые можно использовать для надёжного получения аннотаций, даже при наличии прямых ссылок и других граничных случаев.
Этот модуль поддерживает получение аннотаций в трёх основных форматах
(см. Format), каждый из которых лучше всего подходит для разных случаев использования:
VALUEвычисляет аннотации и возвращает их значение. С ним проще всего работать, но он может вызывать ошибки, например, если аннотации содержат ссылки на неопределённые имена.FORWARDREFвозвращает объектыForwardRefдля аннотаций, которые не удаётся разрешить, что позволяет анализировать аннотации без их вычисления. Это полезно, когда нужно работать с аннотациями, которые могут содержать неразрешённые прямые ссылки.STRINGвозвращает аннотации в виде строки, похожей на то, как они выглядят в исходном файле. Это полезно для генераторов документации, которые хотят отображать аннотации в читаемом виде.
The get_annotations() function is the main entry point for
retrieving annotations. Given a function, class, or module, it returns
an annotations dictionary in the requested format. This module also provides
functionality for working directly with the annotate function
that is used to evaluate annotations, such as get_annotate_from_class_namespace()
and call_annotate_function(), as well as the
call_evaluate_function() function for working with
evaluate functions.
Внимание
Большинство функций этого модуля могут выполнять произвольный код; см. раздел безопасности для получения дополнительной информации.
См. также
PEP 649 предложил текущую модель работы аннотаций в Python.
PEP 749 расширил различные аспекты PEP 649 и представил
модуль annotationlib.
Лучшие практики по аннотациям описывает лучшие практики работы с аннотациями.
typing-extensions предоставляет обратный порт get_annotations(),
который работает в более ранних версиях Python.
Семантика аннотаций¶Annotation semantics
Способ вычисления аннотаций менялся на протяжении истории Python 3, и в настоящее время всё ещё зависит от future import. Существовали следующие модели выполнения для аннотаций:
Стандартная семантика (по умолчанию в 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 и ранее) будет выброшено
NameErrorв строке, где определёнfunc, поскольку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, но это поведение будет
со временем удалено.
Классы¶Classes
- class annotationlib.Format¶
IntEnum, описывающий форматы, в которых могут быть возвращены аннотации. Элементы перечисления или их эквивалентные целочисленные значения можно передавать вget_annotations()и другие функции этого модуля, а также в функции__annotate__.- VALUE = 1¶
Значения – это результат вычисления выражений аннотаций.
- VALUE_WITH_FAKE_GLOBALS = 2¶
Специальное значение, которое используется для сигнализации о том, что функция аннотации выполняется в специальном окружении с поддельными глобальными переменными. При передаче этого значения функции аннотации должны либо вернуть то же значение, что и для формата
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.
Функции¶Functions
- annotationlib.annotations_to_string(annotations)¶
Преобразует словарь аннотаций, содержащий значения времени выполнения, в словарь, содержащий только строки. Если значения ещё не являются строками, они преобразуются с помощью
type_repr(). Это предназначено в качестве помощника для пользовательских функций аннотации, которые поддерживают формат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 с заданным format, членом перечисления
Format, и возвращает словарь аннотаций, полученный от функции.Эта вспомогательная функция необходима, потому что функции аннотации, сгенерированные компилятором для функций, классов и модулей, при прямом вызове поддерживают только формат
VALUE. Для поддержки других форматов эта функция вызывает функцию аннотации в специальном окружении, позволяющем создавать аннотации в других форматах. Это полезный строительный блок при реализации функциональности, требующей частичного вычисления аннотаций во время конструирования класса.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, и используется в поддельном окружении globals. Если ни один из этих форматов не поддерживается, используетсяVALUE. ЕслиVALUEзавершается ошибкой, возбуждается ошибка от этого вызова.
STRING: Если
object.__annotate__существует, сначала вызывается он; в противном случае используетсяobject.__annotations__и преобразуется в строку с помощьюannotations_to_string().При вызове
object.__annotate__сначала он вызывается сSTRING. Если это не реализовано, затем проверяется, поддерживается лиVALUE_WITH_FAKE_GLOBALS, и используется в поддельном окружении globals. Если ни один из этих форматов не поддерживается, используетсяVALUE, а результат преобразуется с помощьюannotations_to_string(). ЕслиVALUEзавершается ошибкой, возбуждается ошибка от этого вызова.
Возвращает словарь.
get_annotations()возвращает новый словарь при каждом вызове; вызов его дважды для одного и того же объекта вернёт два разных, но эквивалентных словаря.Эта функция обрабатывает несколько деталей:
Если eval_str истинно, значения типа
strбудут преобразованы из строки с помощьюeval(). Это предназначено для использования со строковыми аннотациями (from __future__ import annotations). Ошибка – устанавливать eval_str в true с форматами, отличными отFormat.VALUE.Если obj не имеет словаря аннотаций, возвращается пустой словарь. (Функции и методы всегда имеют словарь аннотаций; классы, модули и другие типы вызываемых объектов – не обязательно.)
Игнорирует унаследованные аннотации в классах, а также аннотации в метаклассах. Если класс не имеет собственного словаря аннотаций, возвращается пустой словарь.
Все обращения к членам объекта и значениям словаря выполняются с использованием
getattr()иdict.get()для безопасности.
eval_str управляет тем, заменяются ли значения типа
strрезультатом вызоваeval()для этих значений:Если eval_str истинно,
eval()вызывается для значений типаstr. (Обратите внимание, чтоget_annotations()не перехватывает исключения; еслиeval()возбуждает исключение, оно раскрутит стек за пределы вызоваget_annotations().)Если eval_str ложно (по умолчанию), значения типа
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.
- annotationlib.type_repr(value)¶
Преобразует произвольное значение Python в формат, пригодный для использования форматом
STRING. Для большинства объектов вызываетrepr(), но для некоторых объектов, например объектов-типов, применяется специальная обработка.Это предназначено в качестве вспомогательного средства для предоставляемых пользователем аннотирующих функций, которые поддерживают формат
STRING, но не имеют доступа к коду, создающему аннотации. Его также можно использовать для предоставления удобочитаемого строкового представления для других объектов, содержащих значения, часто встречающиеся в аннотациях.Добавлено в версии 3.14.
Рецепты¶Recipes
Использование аннотаций в метаклассе¶Using annotations in a metaclass
Метакласс может захотеть проверить или даже изменить аннотации в теле класса во время его создания. Для этого требуется извлечь аннотации из словаря пространства имён класса. Для классов, созданных с помощью from __future__ import annotations, аннотации будут находиться по ключу __annotations__ словаря. Для других классов с аннотациями можно использовать get_annotate_from_class_namespace() для получения аннотирующей функции и call_annotate_function() для её вызова и получения аннотаций. Обычно лучше всего использовать формат FORWARDREF, поскольку он позволяет аннотациям ссылаться на имена, которые ещё не могут быть разрешены на момент создания класса.
Для изменения аннотаций лучше всего создать функцию-обёртку для аннотирования, которая вызывает исходную аннотирующую функцию, вносит необходимые изменения и возвращает результат.
Ниже приведён пример метакласса, который отфильтровывает все аннотации 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()
# Для простоты используется строковое сравнение; более надёжное решение
# может использовать 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: # нет аннотаций
classvars = {}
wrapped_annotate = None
typ = super().__new__(mcls, name, bases, ns)
if wrapped_annotate is not None:
# Оборачивается оригинальный __annotate__ в обёртку, удаляющую ClassVars
typ.__annotate__ = wrapped_annotate
typ.classvars = classvars # ClassVars сохраняются в отдельном атрибуте
return typ
Создание пользовательской вызываемой аннотирующей функции¶Creating a custom callable annotate function
Пользовательские аннотирующие функции могут быть буквальными функциями, как те, что автоматически генерируются для функций, классов и модулей. Или же они могут захотеть использовать инкапсуляцию, предоставляемую классами; в этом случае в качестве аннотирующей функции может использоваться любой вызываемый объект.
Чтобы напрямую предоставлять форматы VALUE, STRING или FORWARDREF, аннотирующая функция должна предоставлять следующий атрибут:
Вызываемый объект
__call__с сигнатурой__call__(format, /) -> dict, который не вызывает исключениеNotImplementedErrorпри вызове с поддерживаемым форматом.
Чтобы предоставлять формат VALUE_WITH_FAKE_GLOBALS, который используется для автоматической генерации STRING или FORWARDREF, если они не поддерживаются напрямую, аннотирующие функции должны предоставлять следующие атрибуты:
Вызываемый объект
__call__с сигнатурой__call__(format, /) -> dict, который не вызывает исключениеNotImplementedErrorпри вызове сVALUE_WITH_FAKE_GLOBALS.Объект кода
__code__, содержащий скомпилированный код для аннотирующей функции.Необязательно: кортеж позиционных значений по умолчанию функции
__kwdefaults__, если функция, представленная__code__, использует какие-либо позиционные значения по умолчанию.Необязательно: словарь именованных значений по умолчанию функции
__defaults__, если функция, представленная__code__, использует какие-либо именованные значения по умолчанию.Необязательно: все остальные атрибуты функции.
class Annotate:
called_formats = []
def __call__(self, format=None, /, *, _self=None):
# При вызове с fake globals, `_self` будет
# фактическим значением self, а `self` будет форматом.
if _self is not None:
self, format = _self, self
self.called_formats.append(format)
if format <= 2: # VALUE или 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'}
Или использовать в качестве аннотирующей функции для объекта:
>>> from annotationlib import get_annotations, Format
>>> class C:
... pass
>>> C.__annotate__ = Annotate()
>>> get_annotations(Annotate(), format=Format.STRING)
{'x': 'MyType'}
Ограничения формата STRING¶Limitations of the STRING format
Формат 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.Invert(~),ast.UAdd(+) иast.USub(-) поддерживаютсяast.Not(not) не поддерживается
ast.Dict(за исключением случаев использования распаковки**)ast.Call(за исключением случаев использования**)ast.Constant(хотя это не точное представление константы; например, escape-последовательности в строках теряются; шестнадцатеричные числа преобразуются в десятичные)ast.Attribute(если значение не является константой)ast.Subscript(если значение не является константой)ast.Starred(распаковка*)
Следующее не поддерживается, но при обнаружении преобразователем в строку выдается информативное исключение:
ast.FormattedValue(f-строки; ошибка не обнаруживается, если используются спецификаторы преобразования, такие как!r)ast.JoinedStr(f-строки)
Следующее не поддерживается и приводит к некорректному выводу:
Следующее запрещено в области аннотаций и поэтому не имеет значения:
Ограничения формата FORWARDREF¶Limitations of the FORWARDREF format
Формат 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'}
Последствия для безопасности при интроспекции аннотаций¶Security implications of introspecting annotations
Большая часть функциональности этого модуля связана с выполнением кода, относящегося к аннотациям, который затем может делать произвольные вещи. Например,
get_annotations() может вызвать произвольную функцию annotate, а
ForwardRef.evaluate() может вызвать eval() для произвольной строки. Код, содержащийся в аннотации, может выполнять произвольные системные вызовы, входить в бесконечный цикл или выполнять любые другие операции. Это также верно для любого доступа к атрибуту __annotations__ и для различных функций в модуле typing, работающих с аннотациями, таких как typing.get_type_hints().
Любая проблема безопасности, возникающая из-за этого, также применяется сразу после импорта кода, который может содержать ненадёжные аннотации: импорт кода всегда может привести к выполнению произвольных операций. Однако небезопасно принимать строки или другие входные данные из ненадёжного источника и передавать их любому из API для интроспекции аннотаций, например, редактируя словарь __annotations__ или напрямую создавая объект ForwardRef.