"annotationlib" --- Funcionalidade para introspecção de anotações
*****************************************************************

Adicionado na versão 3.14.

**Código-fonte:** Lib/annotationlib.py

======================================================================

O módulo "annotationlib" fornece ferramentas para introspecção de
*anotações* em módulos, classes e funções.

As anotações são avaliadas preguiçosamente e frequentemente contêm
referências futuras a objetos que ainda não foram definidos quando a
anotação é criada. Este módulo fornece um conjunto de ferramentas de
baixo nível que podem ser usadas para recuperar anotações de forma
confiável, mesmo na presença de referências futuras e outros casos
extremos.

Este módulo oferece suporte a recuperação de anotações em três
formatos principais (veja "Format"), cada um dos quais funciona melhor
para diferentes casos de uso:

* "VALUE" avalia as anotações e retorna seus valores. Isso é mais
  simples de usar, mas pode levantar erros, por exemplo, se as
  anotações contiverem referências a nomes indefinidos.

* "FORWARDREF" retorna objetos "ForwardRef" para anotações que não
  podem ser resolvidas, permitindo que você inspecione as anotações
  sem avaliá-las. Isso é útil quando você precisa trabalhar com
  anotações que podem conter referências de encaminhamento não
  resolvidas.

* "STRING" retorna as anotações como uma string, semelhante a como
  apareceria no arquivo fonte. Isso é útil para geradores de
  documentação que desejam exibir anotações de forma legível.

A função "get_annotations()" é o principal ponto de entrada para
recuperar anotações. Dada uma função, classe ou módulo, ela retorna um
dicionário de anotações no formato solicitado. Este módulo também
fornece funcionalidade para trabalhar diretamente com a *função de
anotação*, usada para avaliar anotações, como
"get_annotate_from_class_namespace()" e "call_annotate_function()",
bem como a função "call_evaluate_function()" para trabalhar com
*funções de avaliação*.

Cuidado:

  Most functionality in this module can execute arbitrary code; see
  the security section for more information.

Ver também:

  **PEP 649** propôs o modelo atual de como as anotações funcionam em
  Python.

  **PEP 749** expandiu vários aspectos de **PEP 649** e introduziu o
  módulo "annotationlib".

  Boas práticas para anotações fornece práticas recomendadas para
  trabalhar com anotações.

  typing-extensions fornece um backport de "get_annotations()" que
  funciona em versões anteriores do Python.


Semânticas de anotação
======================

A forma como as anotações são avaliadas mudou ao longo da história do
Python 3 e atualmente ainda depende de uma importação futura. Houve
modelos de execução para anotações:

* *Semântica de estoque* (padrão no Python 3.0 a 3.13; veja **PEP
  3107** e **PEP 526**): As anotações são avaliadas avidamente, à
  medida que são encontradas no código-fonte.

* *Anotações em string* (usadas com "from __future__ import
  annotations" no Python 3.7 e versões mais recentes; veja **PEP
  563**): As anotações são armazenadas apenas como strings.

* *Avaliação adiada* (padrão no Python 3.14 e versões mais recentes;
  veja **PEP 649** e **PEP 749**): As anotações são avaliadas
  preguiçosamente, somente quando são acessadas.

Como exemplo, considere o seguinte programa:

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

   class Cls: pass

   print(func.__annotations__)

Isso se comportará da seguinte maneira:

* De acordo com a semântica de estoque (Python 3.13 e versões
  anteriores), ele levantará uma "NameError" na linha onde "func" está
  definido, porque "Cls" é um nome indefinido naquele ponto.

* Em anotações stringificadas (se "from __future__ import annotations"
  for usado), exibirá "{'a': 'Cls', 'return': 'None'}".

* Em avaliação adiada (Python 3.14 e posterior), exibirá "{'a': <class
  'Cls'>, 'return': None}".

A semântica de estoque foi usada quando as anotações de função foram
introduzidas pela primeira vez no Python 3.0 (por **PEP 3107**) porque
esta era a maneira mais simples e óbvia de implementar anotações. O
mesmo modelo de execução foi usado quando as anotações de variáveis
foram introduzidas no Python 3.6 (por **PEP 526**). No entanto, a
semântica de estoque causou problemas ao usar anotações como dicas de
tipo, como a necessidade de se referir a nomes que ainda não estavam
definidos quando a anotação era encontrada. Além disso, havia
problemas de desempenho com a execução de anotações no momento da
importação do módulo. Portanto, no Python 3.7, **PEP 563** introduziu
a capacidade de armazenar anotações como strings usando a sintaxe
"from __future__ import annotations". O plano na época era
eventualmente tornar esse comportamento o padrão, mas um problema
surgiu: anotações em string são mais difíceis de processar para
aqueles que inspecionam anotações em tempo de execução. Uma proposta
alternativa, **PEP 649**, introduziu o terceiro modelo de execução,
avaliação adiada, e foi implementada no Python 3.14. Anotações em
string ainda são usadas se "from __future__ import annotations"
estiver presente, mas esse comportamento será eventualmente removido.


Classes
=======

class annotationlib.Format

   Uma "IntEnum" que descreve os formatos nos quais as anotações podem
   ser retornadas. Membros da enumeração, ou seus valores inteiros
   equivalentes, podem ser passados para "get_annotations()" e outras
   funções neste módulo, bem como para funções "__annotate__".

   VALUE = 1

      Os valores são o resultado da avaliação das expressões de
      anotação.

   VALUE_WITH_FAKE_GLOBALS = 2

      Valor especial usado para sinalizar que uma função de anotação
      está sendo avaliada em um ambiente especial com variáveis
      globais falsas. Ao receber este valor, as funções de anotação
      devem retornar o mesmo valor do formato "Format.VALUE" ou
      levantar "NotImplementedError" para sinalizar que não suportam
      execução neste ambiente. Este formato é usado apenas
      internamente e não deve ser passado para as funções deste
      módulo.

   FORWARDREF = 3

      Valores são valores de anotação reais (conforme o formato
      "Format.VALUE") para valores definidos e proxies "ForwardRef"
      para valores indefinidos. Objetos reais podem conter referências
      a objetos proxy "ForwardRef".

   STRING = 4

      Valores são a string de texto da anotação como ela aparece no
      código-fonte, até modificações, incluindo, mas não se limitando
      a, normalizações de espaços em branco e otimizações de valores
      constantes.

      Os valores exatos dessas strings podem mudar em versões futuras
      do Python.

   Adicionado na versão 3.14.

class annotationlib.ForwardRef

   Um objeto proxy para referências futuras em anotações.

   Instâncias desta classe são retornadas quando o formato
   "FORWARDREF" é usado e as anotações contêm um nome que não pode ser
   resolvido. Isso pode acontecer quando uma referência de avanço é
   usada em uma anotação, como quando uma classe é referenciada antes
   de ser definida.

   __forward_arg__

      Uma string contendo o código que foi avaliado para produzir
      "ForwardRef". A string pode não ser exatamente equivalente à
      fonte original.

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

      Avalia a referência futura, retornando seu valor.

      Se o argumento *format* for "VALUE" (o padrão), este método
      poderá levantar uma exceção, como "NameError", caso a referência
      futura se refira a um nome que não pode ser resolvido. Os
      argumentos deste método podem ser usados para fornecer ligações
      para nomes que, de outra forma, seriam indefinidos. Se o
      argumento *format* for "FORWARDREF", o método nunca levantará
      uma exceção, mas poderá retornar uma instância de "ForwardRef".
      Por exemplo, se o objeto de referência futura contiver o código
      "list[undefined]", onde "undefined" é um nome que não está
      definido, avaliá-lo com o formato "FORWARDREF" retornará
      "list[ForwardRef('undefined')]". Se o argumento *format* for
      "STRING", o método retornará "__forward_arg__".

      O parâmetro *owner* fornece o mecanismo preferencial para passar
      informações de escopo para este método. O proprietário de uma
      "ForwardRef" é o objeto que contém a anotação da qual a
      "ForwardRef" deriva, como um objeto módulo, objeto tipo ou
      objeto função.

      Os parâmetros *globals*, *locals* e *type_params* fornecem um
      mecanismo mais preciso para influenciar os nomes disponíveis
      quando "ForwardRef" é avaliado. *globals* e *locals* são
      passados para "eval()", representando os espaços de nomes global
      e local nos quais o nome é avaliado. O parâmetro *type_params* é
      relevante para objetos criados usando a sintaxe nativa para
      classes genéricas e funções. É uma tupla de parâmetros de tipo
      que estão no escopo enquanto a referência futura está sendo
      avaliada. Por exemplo, ao avaliar um "ForwardRef" recuperado de
      uma anotação encontrada no espaço de nomes de classe de uma
      classe genérica "C", *type_params* deve ser definido como
      "C.__type_params__".

      Instâncias de "ForwardRef" retornadas por "get_annotations()"
      retêm referências a informações sobre o escopo de onde se
      originaram, portanto, chamar esse método sem argumentos
      adicionais pode ser suficiente para avaliar tais objetos.
      Instâncias de "ForwardRef" criadas por outros meios podem não
      ter nenhuma informação sobre seu escopo, portanto, passar
      argumentos para esse método pode ser necessário para avaliá-las
      com sucesso.

      Se nenhum entre *owner*, *globals*, *locals* ou *type_params*
      for fornecido e "ForwardRef" não contiver informações sobre sua
      origem, dicionários globals e locals vazios serão usados.

   Adicionado na versão 3.14.


Funções
=======

annotationlib.annotations_to_string(annotations)

   Converte um dicionário de anotações contendo valores de tempo de
   execução em um dicionário contendo apenas strings. Se os valores
   ainda não forem strings, eles serão convertidos usando
   "type_repr()". Isso serve como um auxiliar para funções de anotação
   fornecidas pelo usuário que oferecem suporte ao formato "STRING",
   mas não têm acesso ao código que cria as anotações.

   Por exemplo, isso é usado para implementar o "STRING" para classes
   "typing.TypedDict" criadas por meio da sintaxe funcional:

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

   Adicionado na versão 3.14.

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

   Chama a *função de anotação* *annotate* com o *format* fornecido,
   um membro da enumeração "Format" e retorne o dicionário de
   anotações produzido pela função.

   Esta função auxiliar é necessária porque as funções de anotação
   geradas pelo compilador para funções, classes e módulos oferecem
   suporte a apenas o formato "VALUE" quando chamadas diretamente.
   Para oferecer suporte a outros formatos, esta função chama a função
   de anotação em um ambiente especial que permite a produção de
   anotações nos outros formatos. Este é um bloco de construção útil
   ao implementar funcionalidades que precisam avaliar anotações
   parcialmente enquanto uma classe está sendo construída.

   *owner* é o objeto que possui a função de anotação, geralmente uma
   função, classe ou módulo. Se fornecido, é usado no formato
   "FORWARDREF" para produzir um objeto "ForwardRef" que contém mais
   informações.

   Ver também:

     **PEP 649** contém uma explicação da técnica de implementação
     usada por esta função.

   Adicionado na versão 3.14.

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

   Chama a *função de avaliação* *evaluate* com o *format* fornecido,
   um membro da enumeração "Format" e retorna o valor produzido pela
   função. Isso é semelhante a "call_annotate_function()", mas esta
   última sempre retorna um dicionário que mapeia strings para
   anotações, enquanto esta função retorna um único valor.

   Isso se destina ao uso com as funções de avaliação geradas para
   elementos avaliados preguiçosamente relacionados a apelidos de tipo
   e parâmetros de tipo:

   * "typing.TypeAliasType.evaluate_value()", o valor dos apelidos de
     tipo

   * "typing.TypeVar.evaluate_bound()", a delimitação das variáveis de
     tipo

   * "typing.TypeVar.evaluate_constraints()", as restrições de tipos
     variáveis

   * "typing.TypeVar.evaluate_default()", o valor padrão de tipos
     variáveis

   * "typing.ParamSpec.evaluate_default()", o valor padrão de
     especificações de parâmetros

   * "typing.TypeVarTuple.evaluate_default()", o valor padrão de
     tuplas de tipos variáveis

   *owner* é o objeto que possui a função de avaliação, como o apelido
   de tipo ou o objeto de tipo variável.

   *format* pode ser usado para controlar o formato no qual o valor é
   retornado:

      >>> 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'

   Adicionado na versão 3.14.

annotationlib.get_annotate_from_class_namespace(namespace)

   Recupera a *função de anotação* de um dicionário de espaço de nomes
   de classe *namespace*. Retorna "None" se o espaço de nomes não
   contiver uma função de anotação. Isso é útil principalmente antes
   da classe ser totalmente criada (por exemplo, em uma metaclasse);
   após a classe existir, a função de anotação pode ser recuperada com
   "cls.__annotate__". Veja abaixo para um exemplo usando esta função
   em uma metaclasse.

   Adicionado na versão 3.14.

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

   Calcula o dicionário de anotações para um objeto.

   *obj* pode ser um objeto chamável, classe, módulo ou outro objeto
   com os atributos "__annotate__" ou "__annotations__". Passar
   qualquer outro objeto levanta "TypeError".

   O parâmetro *format* controla o formato em que as anotações são
   retornadas e deve ser um membro da enumeração "Format" ou seu
   equivalente inteiro. Os diferentes formatos funcionam da seguinte
   forma:

   * VALUE: "object.__annotations__" é tentado primeiro; se não
     existir, a função "object.__annotate__" é chamada, se existir.

   * FORWARDREF: Se "object.__annotations__" existir e puder ser
     avaliado com sucesso, ele será usado; caso contrário, a função
     "object.__annotate__" será chamada. Se também não existir,
     "object.__annotations__" será tentado novamente e qualquer erro
     ao acessá-lo será levantado novamente.

   * STRING: Se "object.__annotate__" existir, ele será chamado
     primeiro; caso contrário, "object.__annotations__" será usado e
     transformado em string usando "annotations_to_string()".

   Retorna um dict. "get_annotations()" retorna um novo dict toda vez
   que é chamado; chamá-lo duas vezes no mesmo objeto retornará dois
   dicts diferentes, mas equivalentes.

   Esta função cuida de vários detalhes para você:

   * Se *eval_str* for verdadeiro, valores do tipo "str" serão
     descodificados usando "eval()". Isso se destina ao uso com
     anotações transformadas em string ("from __future__ import
     annotations"). É um erro definir *eval_str* como true com
     formatos diferentes de "Format.VALUE".

   * Se *obj* não tiver um dicionário de anotações, retorna um
     dicionário vazio. (Funções e métodos sempre têm um dicionário de
     anotações; classes, módulos e outros tipos de chamáveis podem não
     ter.)

   * Ignora anotações herdadas em classes, bem como anotações em
     metaclasses. Se uma classe não tiver seu próprio dicionário de
     anotações, retorna um dicionário vazio.

   * Todos os acessos aos membros do objeto e valores do dicionário
     são feitos usando "getattr()" e "dict.get()" por segurança.

   *eval_str* controla se valores do tipo "str" são substituídos ou
   não pelo resultado da chamada de "eval()" nesses valores:

   * Se eval_str for verdadeiro, "eval()" será chamado em valores do
     tipo "str". (Observe que "get_annotations()" não captura
     exceções; se "eval()" levanta uma exceção, ele desenrolará a
     pilha após a chamada de "get_annotations()".)

   * Se *eval_str* for falso (o padrão), os valores do tipo "str" não
     serão alterados.

   *globals* e *locals* são passados para "eval()"; consulte a
   documentação de "eval()" para mais informações. Se *globals* ou
   *locals* for "None", esta função pode substituir esse valor por um
   padrão específico do contexto, dependendo de "type(obj)":

   * Se *obj* for um módulo, *globals* assume como padrão
     "obj.__dict__".

   * Se *obj* for uma classe, *globals* assume como padrão
     "sys.modules[obj.__module__].__dict__" e *locals* assume como
     padrão o espaço de nomes da classe *obj*.

   * Se *obj* for um chamável, *globals* assume como padrão
     "obj.__globals__", embora se *obj* for uma função encapsulada
     (usando "functools.update_wrapper()") ou um objeto
     "functools.partial", ela será desencapsulada até que uma função
     não encapsulada seja encontrada.

   Chamar "get_annotations()" é uma boa prática para acessar o
   dicionário de anotações de qualquer objeto. Consulte Boas práticas
   para anotações para obter mais informações sobre as práticas
   recomendadas para anotações.

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

   Adicionado na versão 3.14.

annotationlib.type_repr(value)

   Converte um valor Python arbitrário para um formato adequado para
   uso pelo formato "STRING". Isso chama "repr()" para a maioria dos
   objetos, mas tem um tratamento especial para alguns objetos, como
   objetos tipo.

   Isto serve como um auxiliar para funções de anotação fornecidas
   pelo usuário que oferecem suporte ao formato "STRING", mas não têm
   acesso ao código que cria as anotações. Também pode ser usado para
   fornecer uma representação de string amigável para outros objetos
   que contêm valores comumente encontrados em anotações.

   Adicionado na versão 3.14.


Receitas
========


Usando anotações em uma metaclasse
----------------------------------

Uma metaclasse pode querer inspecionar ou até mesmo modificar as
anotações no corpo de uma classe durante a criação da classe. Isso
requer a recuperação das anotações do dicionário de espaços de nomes
da classe. Para classes criadas com "from __future__ import
annotations", as anotações estarão na chave "__annotations__" do
dicionário. Para outras classes com anotações,
"get_annotate_from_class_namespace()" pode ser usado para obter a
função annotate, e "call_annotate_function()" pode ser usado para
chamá-la e recuperar as anotações. Usar o formato "FORWARDREF"
geralmente é a melhor opção, pois permite que as anotações se refiram
a nomes que ainda não podem ser resolvidos quando a classe é criada.

Para modificar as anotações, é melhor criar uma função de anotação que
chame a função de anotação original, faça os ajustes necessários e
retorne o resultado.

Abaixo está um exemplo de uma metaclasse que filtra todas as anotações
"typing.ClassVar" da classe e as coloca em um atributo separado:

   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 comparação de strings para simplificar; uma solução
               # mais robusta poderia usar 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:  # nenhuma anotação
            classvars = {}
            wrapped_annotate = None
         typ = super().__new__(mcls, name, bases, ns)

         if wrapped_annotate is not None:
            # Encapsula o __annotate__ original com um invólucro que remove ClassVars
            typ.__annotate__ = wrapped_annotate
         typ.classvars = classvars  # Armazena as ClassVars em um atributo separado
         return typ


Limitações do formato "STRING"
==============================

O formato "STRING" tem como objetivo aproximar o código-fonte da
anotação, mas a estratégia de implementação usada significa que nem
sempre é possível recuperar o código-fonte exato.

Primeiro, a transformação em string, é claro, não pode recuperar
nenhuma informação que não esteja presente no código compilado,
incluindo comentários, espaços em branco, parênteses e operações que
são simplificadas pelo compilador.

Em segundo lugar, a transformação em string pode interceptar quase
todas as operações que envolvem nomes pesquisados em algum escopo, mas
não pode interceptar operações que operem totalmente em constantes.
Como corolário, isso também significa que não é seguro solicitar o
formato "STRING" em código não confiável: Python é poderoso o
suficiente para permitir a execução arbitrária de código mesmo sem
acesso a nenhuma variável global ou embutida. Por exemplo:

   >>> 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'}

Nota:

  Este exemplo específico funciona no momento em que este artigo foi
  escrito, mas depende de detalhes de implementação e não há garantia
  de que funcionará no futuro.

Entre os diferentes tipos de expressões que existem em Python,
conforme representado pelo módulo "ast", algumas expressões são
suportadas, o que significa que o formato "STRING" geralmente pode
recuperar o código-fonte original; outras não são suportadas, o que
significa que podem resultar em saída incorreta ou erro.

Os seguintes são aceitos (às vezes com ressalvas):

* "ast.BinOp"

* "ast.UnaryOp"

  * "ast.Invert" ("~"), "ast.UAdd" ("+") e "ast.USub" ("-") são
    suportadas

  * "ast.Not" ("not") não é suportada

* "ast.Dict" (exceto ao usar desempacotamento "**")

* "ast.Set"

* "ast.Compare"

  * "ast.Eq" e "ast.NotEq" são suportadas

  * "ast.Lt", "ast.LtE", "ast.Gt" e "ast.GtE" são suportadas, mas o
    operando pode ser invertido

  * "ast.Is", "ast.IsNot", "ast.In" e "ast.NotIn" não são suportadas

* "ast.Call" (exceto ao usar desempacotamento "**")

* "ast.Constant" (embora não seja a representação exata da constante;
  por exemplo, sequências de escape em strings são perdidas; números
  hexadecimais são convertidos em decimais)

* "ast.Attribute" (presumindo que o valor não é uma constante)

* "ast.Subscript" (presumindo que o valor não é uma constante)

* "ast.Starred" (desempacotamento "*")

* "ast.Name"

* "ast.List"

* "ast.Tuple"

* "ast.Slice"

Os seguintes não são suportados, mas geram um erro informativo quando
encontrados pela transformação em string:

* "ast.FormattedValue" (f-strings; o erro não é detectado se
  especificadores de conversões como "!r" forem usados)

* "ast.JoinedStr" (f-strings)

Os seguintes itens não são suportados e resultam em saída incorreta:

* "ast.BoolOp" ("and" e "or")

* "ast.IfExp"

* "ast.Lambda"

* "ast.ListComp"

* "ast.SetComp"

* "ast.DictComp"

* "ast.GeneratorExp"

Os seguintes itens não são permitidos em escopos de anotação e,
portanto, não são relevantes:

* "ast.NamedExpr" (":=")

* "ast.Await"

* "ast.Yield"

* "ast.YieldFrom"


Limitações do formato "FORWARDREF"
==================================

O formato "FORWARDREF" visa produzir valores reais o máximo possível,
com tudo o que não puder ser resolvido sendo substituído por objetos
"ForwardRef". Ele é afetado, em linhas gerais, pelas mesmas limitações
do formato "STRING": anotações que realizam operações em literais ou
que usam tipos de expressão não suportados podem levantar exceções
quando avaliadas usando o formato "FORWARDREF".

Abaixo estão alguns exemplos do comportamento com expressões não
suportadas:

   >>> 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
==================================================

Much of the functionality in this module involves executing code
related to annotations, which can then do arbitrary things. For
example, "get_annotations()" may call an arbitrary *annotate
function*, and "ForwardRef.evaluate()" may call "eval()" on an
arbitrary string. Code contained in an annotation might make arbitrary
system calls, enter an infinite loop, or perform any other operation.
This is also true for any access of the "__annotations__" attribute,
and for various functions in the "typing" module that work with
annotations, such as "typing.get_type_hints()".

Any security issue arising from this also applies immediately after
importing code that may contain untrusted annotations: importing code
can always cause arbitrary operations to be performed. However, it is
unsafe to accept strings or other input from an untrusted source and
pass them to any of the APIs for introspecting annotations, for
example by editing an "__annotations__" dictionary or directly
creating a "ForwardRef" object.
