O módulo annotationlib no Python, adicionado no Python 3.14, oferece ferramentas de baixo nível para recuperar e avaliar anotações de funções, classes e módulos. Ele foi criado para lidar com o novo modelo de avaliação adiada, referências futuras e casos em que bibliotecas precisam inspecionar tipos sem forçar todos os nomes a existir imediatamente.
Apesar do nome, o módulo não é apenas um leitor de dicionários. Muitas operações podem executar expressões presentes nas anotações. Por isso, introspecção de anotações deve ser tratada como execução de código, principalmente em frameworks, plugins, geradores de documentação e sistemas que recebem dados externos.
Por que o modelo mudou
Até o Python 3.13, anotações eram normalmente avaliadas quando a função ou classe era definida. Isso causava problemas com nomes declarados depois e aumentava o custo de importação. A opção from __future__ import annotations armazenava textos, mas dificultava a introspecção de runtime.
No Python 3.14, o modelo padrão usa avaliação adiada. As expressões são avaliadas quando alguém acessa as anotações. O annotationlib fornece uma interface consistente para esse cenário.
A função get_annotations
A principal entrada é get_annotations(). Ela aceita função, classe, módulo ou outro objeto compatível e sempre devolve um novo dicionário.
from annotationlib import get_annotations
def calcular(valor: int, escala: float) -> float:
return valor * escala
print(get_annotations(calcular))
# {'valor': <class 'int'>, 'escala': <class 'float'>,
# 'return': <class 'float'>}Usar essa função é preferível a acessar __annotations__ diretamente porque ela lida com funções de anotação, classes, wrappers e detalhes do modelo adiado.
Três formatos principais
A enumeração Format define como os resultados são produzidos:
VALUEavalia e devolve objetos reais.FORWARDREFdevolve objetos reais quando possível e proxies para nomes não resolvidos.STRINGproduz representações textuais aproximadas.
from annotationlib import Format, get_annotations
def processar(item: TipoDeclaradoDepois) -> list[str]:
...
print(get_annotations(processar, format=Format.FORWARDREF))
print(get_annotations(processar, format=Format.STRING))VALUE é simples para validação de runtime, mas pode gerar NameError. FORWARDREF é útil em frameworks e STRING costuma servir melhor para documentação.
Formato VALUE
Quando todos os nomes existem, VALUE devolve classes, aliases e objetos de typing já avaliados.
from annotationlib import Format, get_annotations
class Usuario:
pass
def salvar(usuario: Usuario) -> None:
pass
anotacoes = get_annotations(salvar, format=Format.VALUE)
assert anotacoes['usuario'] is UsuarioA avaliação pode chamar código arbitrário contido na expressão. Não use o formato em anotações provenientes de fonte não confiável.
Formato FORWARDREF
Quando um nome não pode ser resolvido, o módulo cria um ForwardRef.
from annotationlib import Format, ForwardRef, get_annotations
def carregar(valor: ModeloFuturo) -> ModeloFuturo:
pass
resultado = get_annotations(
carregar,
format=Format.FORWARDREF,
)
print(resultado)O proxy mantém o código associado e, quando obtido por get_annotations(), pode guardar informações de escopo.
Avalie uma referência futura
ForwardRef.evaluate() tenta resolver o nome. Você pode fornecer o objeto proprietário ou namespaces explícitos.
referencia = resultado['valor']
class ModeloFuturo:
pass
valor = referencia.evaluate(
globals=globals(),
locals=locals(),
)
assert valor is ModeloFuturoCom format=Format.FORWARDREF, uma parte ainda não resolvida pode continuar como proxy. Com STRING, o método devolve a representação textual.
Formato STRING
STRING tenta aproximar a expressão escrita na fonte.
texto = get_annotations(
salvar,
format=Format.STRING,
)
print(texto)
# {'usuario': 'Usuario', 'return': 'None'}O resultado não é uma cópia exata. Espaços, comentários, parênteses e representações de constantes podem mudar. Algumas expressões não são suportadas ou produzem resultado aproximado.
STRING também pode executar código
É um erro pensar que o formato textual é seguro. A implementação pode executar operações da anotação para reconstruir a representação. Expressões construídas com atributos, chamadas ou constantes podem produzir efeitos colaterais.
Não aceite uma string de usuário, coloque-a em __annotations__ e passe ao módulo. Para documentação de código externo, analise a fonte com AST em um processo isolado.
eval_str e anotações antigas
Quando código usa from __future__ import annotations, valores podem estar armazenados como strings. eval_str=True pede que get_annotations() execute eval() nesses valores.
anotacoes = get_annotations(
objeto,
eval_str=True,
globals=namespace_global,
locals=namespace_local,
)Essa opção só é compatível com Format.VALUE e possui os riscos normais de eval(). Use apenas com código confiável.
annotations_to_string
annotations_to_string() converte um dicionário de valores de runtime em strings adequadas para exibição.
from annotationlib import annotations_to_string
resultado = annotations_to_string({
'item': list[int],
'return': type(None),
})
print(resultado)A função usa type_repr() para tratar classes e objetos comuns. Ela é útil para funções de anotação customizadas que não têm acesso ao código-fonte original.
type_repr
type_repr() cria uma representação voltada a tipos e anotações.
from annotationlib import type_repr
print(type_repr(dict[str, int]))O resultado é para interface e documentação, não para serialização estável. Não dependa do texto como identificador permanente.
Funções __annotate__
No modelo adiado, compilador pode criar uma função __annotate__ que produz o dicionário quando solicitada. call_annotate_function() chama essa função em um dos formatos suportados.
from annotationlib import call_annotate_function, Format
anotacoes = call_annotate_function(
MinhaClasse.__annotate__,
Format.FORWARDREF,
owner=MinhaClasse,
)Bibliotecas normais devem preferir get_annotations(). A API direta é destinada a frameworks, metaclasses e implementações de typing.
Anotações durante a criação de classes
Uma metaclasse ainda não possui o objeto final quando recebe o namespace. get_annotate_from_class_namespace() recupera a função de anotação desse dicionário.
import annotationlib
class MinhaMeta(type):
def __new__(mcls, nome, bases, namespace):
annotate = annotationlib.get_annotate_from_class_namespace(
namespace
)
if annotate:
dados = annotationlib.call_annotate_function(
annotate,
annotationlib.Format.FORWARDREF,
)
validar_campos(dados)
return super().__new__(mcls, nome, bases, namespace)Esse recurso ajuda frameworks de modelos, ORMs e validadores. Evite avaliar valores completamente antes de a classe estar pronta.
Funções evaluate
call_evaluate_function() trabalha com funções preguiçosas associadas a aliases e parâmetros de tipo. Ela pode retornar valor, ForwardRef ou string.
valor = annotationlib.call_evaluate_function(
Alias.evaluate_value,
annotationlib.Format.FORWARDREF,
owner=Alias,
)Essa é uma API avançada, especialmente relevante para bibliotecas que implementam typing e genéricos.
Integração com inspect
O guia de inspect no Python mostra assinaturas e objetos vivos. Em Python 3.14, bibliotecas de introspecção podem combinar inspect.signature() com annotationlib.get_annotations() para controlar o formato e referências futuras.
Escopos e nomes
O módulo escolhe namespaces padrão de acordo com o objeto. Módulos usam seu dicionário; classes usam o módulo e o namespace da classe; funções usam seus globais, inclusive após desembrulhar decorators conhecidos.
Para entender como o compilador classifica nomes locais, globais, livres e parâmetros de tipo, veja symtable no Python.
Wrappers e partial
get_annotations() conhece wrappers criados com functools.update_wrapper() e objetos partial. Isso reduz a chance de consultar o namespace errado em funções decoradas. Decorators customizados ainda devem preservar __wrapped__ corretamente.
Classes não herdam anotações automaticamente
A função ignora anotações herdadas e metaclasses. Se uma classe não possui seu próprio dicionário, o resultado é vazio. Para construir um esquema completo de herança, percorra o MRO deliberadamente e defina regras de sobrescrita.
O guia de types no Python apresenta utilitários relacionados a objetos e tipos de runtime.
Compatibilidade entre versões
annotationlib foi adicionado no Python 3.14. Bibliotecas que suportam versões anteriores precisam usar verificações de versão ou o backport fornecido por typing_extensions, quando apropriado.
Não importe o módulo incondicionalmente em um pacote que declara compatibilidade com 3.13 ou anterior.
Segurança
- Anotações são expressões Python e podem executar código.
ForwardRef.evaluate()pode chamareval().STRINGnão é uma sandbox.- Não processe anotações construídas com entrada externa.
- Limite tempo e recursos em ferramentas de documentação.
- Não exponha globais sensíveis ao avaliar.
- Execute análise de plugins em processo separado.
Testes recomendados
Teste nomes definidos posteriormente, aliases, genéricos, parâmetros de tipo, classes decoradas, funções embrulhadas, future annotations, nomes inexistentes e anotações com efeitos colaterais. Verifique os três formatos separadamente.
Inclua testes por versão do Python porque semântica e representações podem evoluir. O formato textual não deve ser comparado com espaços e parênteses exatos.
Boas práticas
- Use
get_annotations()como entrada principal. - Escolha explicitamente o formato.
- Prefira
FORWARDREFdurante construção de classes. - Use
STRINGapenas para apresentação. - Não presuma que nenhuma avaliação ocorrerá.
- Trate exceções de nomes e da própria expressão.
- Mantenha fallback para versões anteriores.
- Documente a política de segurança do framework.
Conclusão
O annotationlib no Python organiza a introspecção de anotações no modelo de avaliação adiada do Python 3.14. Ele permite obter valores reais, referências futuras ou textos, além de oferecer APIs avançadas para metaclasses e typing.
Seu poder exige cautela: consultar anotações pode executar código arbitrário. Consulte a documentação oficial do annotationlib, a PEP 649 e a PEP 749.







